501. 二叉搜索树中的众数
思路分析:
-
找众数,说明要统计出现次数,一般会直接想到搞个哈希表来计数(就像后面的方法二)。但是如果一个有序数组中统计出现次数,使用双指针就能很好解决,类似的这里给我们的树不是一般的树,而是BST,中序遍历相当于遍历有序数组。
-
利用BST这个特性,我们不需要使用哈希表来计数。如同双指针的做法,这里也需要记录上一个结点TreeNode pre;这样才能知道当前结点值与谁比较;另外还需要记录某个值的出现次数curCount,以及出现次数的最大值maxCount(否则你咋知道谁出现最多次)。并且这里遍历过程中的众数信息需要记录(List存放众数)及更新。
在中序遍历中:
-
如果pre == null,说明这是遍历的第一个结点,不需要处理(第一个结点的初条件在主函数中设定)。
-
如果当前结点值与上一个结点值相等,那么这个数字的出现次数+1。
-
否则,我们先去判断,上一个数字的出现次数curCount与之前的最大出现次数maxCount谁更大:
-
如果上一个数字出现次数最大,需要更新众数信息。首先更新最大出现次数maxCount = curCount;。然后将之前记录的众数清空,再将上一个数字放入items.clear(); items.add(pre.val);
如果一个数字出现次数等于最大出现次数,那么目前来看,它也是可能的众数,加入列表items.add(pre.val);
否则,上一个数字一定不是众数,不管它,继续保留List中的数字。
最后,重置计数curCount = 1;,表示当前数字出现一次了。
然后更新pre = x;
回到主函数:
-
特殊情况处理,root == null直接返回new int[0]。
-
然后上文提到的初始化,因为最左边结点在中序的递归中不处理,所以,我们要首先将maxCount = 1,因为树非空总会有数字出现一次,然后curCount = 1,代表最左边结点被我们在主函数计数了。
-
调用辅助函数后,还需要处理最后一个结点的值,因为在递归中不会有再下一个结点值与之不相等然后来判断它的值是否为众数。
所以如果curCount > maxCount,说明最后一个结点才是唯一众数,return new int[]{pre.val};
如果curCount == maxCount,说明最后一个结点也是众数,items.add(pre.val);
最后,将List转成数组返回。
这个题要注意边界的考虑! -
时间复杂度为O(n)O(n), 不考虑递归调用栈的话,空间复杂度可以认为是常数,中间状态List的元素并不会很多。
package LeetCode.FiveHundredOneToOneThousand;
import LeetCode.TreeNode;
import java.util.ArrayList;
import java.util.List;
public class FiveHundredAndOne {
private List<Integer> items; // 众数数组
private int maxCount; // 最大值
private int curCount; // 记录当前值的个数
private TreeNode pre; // 记录上个节点,判断是否与下个结点的值相同
public int[] findMode(TreeNode root) {
if (root == null)
return new int[0];
items = new ArrayList<>();
// 这里设置为1是因为 在递归中 BST中最左边的结点被跳过了,作为初状态 该结点值出现一次,记录的出现最多次数一开始也为1
maxCount = 1;
curCount = 1;
midTraversal(root);
// 最右端结点的处理,从递归中看
// 最后一个结点与前一个结点相等只更新了curCount的值
// 不相等则只考虑到倒数第二个结点。
if(curCount > maxCount)
return new int[]{pre.val};
if(curCount == maxCount)
items.add(pre.val);
int[] res = new int[items.size()];
for (int i = 0; i < res.length; i++)
res[i] = items.get(i);
return res;
}
// 因为是BST树,中序遍历就是直接将其排序
private void midTraversal(TreeNode x) {
if (x == null) return;
midTraversal(x.left);
if(pre != null){
if(x.val != pre.val){ // 说明上一个值的结点数量已经统计完成 要看出现次数的情况
if(curCount > maxCount){ // 出现次数更多,清空之前的出现少的数,更新最大出现次数
maxCount = curCount;
items.clear();
items.add(pre.val);
}
else if(curCount == maxCount){ // 不止一个众数
items.add(pre.val);
}
curCount = 1; // 当前值与上一个结点值不同,重置计数
}
else curCount++; // 当前值与上一个结点值相同,当前值的出现次数增加。
}
pre = x;
midTraversal(x.right);
}
}
上一篇: LeetCode200. 岛屿的个数
下一篇: 二叉树构建、深度遍历、广度优先遍历
推荐阅读
-
Java实现的二叉树常用操作【前序建树,前中后递归非递归遍历及层序遍历】
-
Python实现查找二叉搜索树第k大的节点功能示例
-
Java创建二叉搜索树,实现搜索,插入,删除的操作实例
-
Java实现的二叉树常用操作【前序建树,前中后递归非递归遍历及层序遍历】
-
Python中的二叉树查找算法模块使用指南
-
Python利用前序和中序遍历结果重建二叉树的方法
-
Python实现查找二叉搜索树第k大的节点功能示例
-
C语言实现线索二叉树的前中后创建和遍历详解
-
荐 Java刷题笔记15:不同的二叉搜索树( Unique Binary Search Trees)
-
Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作示例