摸了一个寒假了,得赶一赶了
251.二叉搜索树中的众数
给你一个含重复值的二叉搜索树(BST)的根节点 root
,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义:
- 结点左子树中所含节点的值 小于等于 当前节点的值
- 结点右子树中所含节点的值 大于等于 当前节点的值
- 左子树和右子树都是二叉搜索树
示例 1:
输入:root = [1,null,2,2] 输出:[2]
示例 2:
输入:root = [0] 输出:[0]
提示:
- 树中节点的数目在范围
[1, 104]
内 -105 <= Node.val <= 105
进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)
思路
哈希表中序遍历
还记得二叉搜索树的遍历用的是二叉树的中序遍历的特性,在遍历二叉搜索树时是从小到大的顺序遍历的,所以我们需要在遍历的过程中找到所有的众数并且加入结果中,在遍历过程中将每个数的出现次数存入HashMap,并记录最大值,最后遍历hashmap并将键值为最大值的所有key加入ArrayList,最后转为数组。
class Solution {public int[] findMode(TreeNode root) {Map<Integer, Integer> map = new HashMap<>();Stack<TreeNode> stack = new Stack<>();TreeNode cur = root;int max = 0;while (cur != null || !stack.isEmpty()) {if (cur != null) {stack.push(cur);cur = cur.left;} else {cur = stack.pop();map.put(cur.val, map.getOrDefault(cur.val, 0) + 1);if (map.get(cur.val) > max)max = map.get(cur.val);cur = cur.right;}}List<Integer> modes = new ArrayList<>();for (Map.Entry<Integer, Integer> entry : map.entrySet()) {if (entry.getValue() == max) {modes.add(entry.getKey());}}return modes.stream().mapToInt(i -> i).toArray();}}
此处将ArrayList转换为int数组使用了java流的方法:
但是这个解法我写的非常难绷,差点气笑了,纯纯狗屎。
中序遍历法(真)
官方题解第一句话直接否定了我的若只解法,哈哈,真是一场酣畅淋漓的破防啊,活个屁啊,跳了
其实也没高明到哪里去,只是在遍历的过程中更新答案数组。用base记录当前数字,count记录当前数字重复次数,用maxCount记录当前已经扫描过的数中出现最多的数的次数,answer代表众数数组。每次扫描到一个新元素:
首先更新base和count:
如果该元素与base相同,那么count自增1;
如果不同,base更新为当前数字,count复位为1。
然后更新maxcount:
如果count=maxcount,那么说明当前数字也为当前众数,将base加入answer
如果count>maxcount,那么将answer清空,再将base加入answer。
class Solution {List<Integer> answer=new ArrayList<>();int base,count,maxCount;public int[] findMode(TreeNode root) {dfs(root);int[] mode=new int[answer.size()];for(int i=0;i<answer.size();++i){mode[i]=answer.get(i);}return mode;}public void dfs(TreeNode node){if(node==null){return;}dfs(node.left);update(node.val);dfs(node.right);}public void update(int i){if(i==base){count++;}else {count=1;base=i;}if(count==maxCount){answer.add(base);}if(count>maxCount){maxCount=count;answer.clear();answer.add(i);}}}
以后遍历二叉树还是写递归吧,看着就清爽。
Morris中序遍历
最后来解决进阶问题:如何将空间复杂度控制在O(1)?
从前两个方法的代码中我们可以发现,主要的空间消耗在于中序遍历,不管是递归回溯或者是迭代法用栈存储返回点,都需要付出额外的空间代价。
Morris中序遍历:
其实没太看懂。
class Solution {List<Integer> answer = new ArrayList<>();int base, count, maxCount;public int[] findMode(TreeNode root) {TreeNode cur = root, pre = null;while (cur != null) {if (cur.left == null) {update(cur.val);cur = cur.right;continue;}pre = cur.left;while (pre.right != null && pre.right != cur) {pre = pre.right;}if (pre.right == null) {pre.right = cur;cur = cur.left;} else {pre.right = null;update(cur.val);cur = cur.right;}}int[] mode = new int[answer.size()];for (int i = 0; i < answer.size(); ++i) {mode[i] = answer.get(i);}return mode;}public void update(int i) {if (i == base) {count++;} else {count = 1;base = i;}if (count == maxCount) {answer.add(base);}if (count > maxCount) {maxCount = count;answer.clear();answer.add(i);}}}
懂了吗?
总结
一个多月没写,手生了很多,接下来一天两题吧