给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有众数(出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义:
- 结点左子树中所含节点的值 小于等于 当前节点的值
- 结点右子树中所含节点的值 大于等于 当前节点的值
- 左子树和右子树都是二叉搜索树
示例 1:
输入:root = [1,null,2,2]
输出:[2]
示例 2:
输入:root = [0]
输出:[0]
众数(出现频率最高的元素),那么可以先统计值得出现频率次数
public int[] findMode(TreeNode root) {Map<Integer, Integer> timesMap = new HashMap<>();// 遍历二叉树时统计值的出现次数order(root, timesMap);}/*** 遍历二叉树时统计值的次数* @param node*/private void order(TreeNode node, Map<Integer, Integer> timesMap) {if (node == null) {return;}timesMap.put(node.val, timesMap.getOrDefault(node.val, 0) + 1);order(node.left);order(node.right);}
遍历次数结果Map,得到次数最大值
public int[] findMode(TreeNode root) {Map<Integer, Integer> timesMap = new HashMap<>();// 遍历二叉树时统计值的出现次数order(root, timesMap);// 找到次数最大值int maxTimes = 0;for (Map.Entry<Integer, Integer> entry : timesMap.entrySet()) {maxTimes = Math.max(entry.getValue(), maxTimes);}}/*** 遍历二叉树时统计值的次数* @param node*/private void order(TreeNode node, Map<Integer, Integer> timesMap) {if (node == null) {return;}timesMap.put(node.val, timesMap.getOrDefault(node.val, 0) + 1);order(node.left);order(node.right);}
再次遍历次数结果Map,找到出现最大次数的值,放入 List
public int[] findMode(TreeNode root) {Map<Integer, Integer> timesMap = new HashMap<>();// 遍历二叉树时统计值的出现次数order(root, timesMap);// 找到次数最大值int maxTimes = 0;for (Map.Entry<Integer, Integer> entry : timesMap.entrySet()) {maxTimes = Math.max(entry.getValue(), maxTimes);}// 找到出现次数最多的值List<Integer> maxTimesValList = new ArrayList<>();for (Map.Entry<Integer, Integer> entry : timesMap.entrySet()) {if (entry.getValue() == maxTimes) {maxTimesValList.add(entry.getKey());}}}/*** 遍历二叉树时统计值的次数* @param node*/private void order(TreeNode node, Map<Integer, Integer> timesMap) {if (node == null) {return;}timesMap.put(node.val, timesMap.getOrDefault(node.val, 0) + 1);order(node.left);order(node.right);}
最后将 List 转为 array
public int[] findMode(TreeNode root) {Map<Integer, Integer> timesMap = new HashMap<>();// 遍历二叉树时统计值的出现次数order(root, timesMap);// 找到次数最大值int maxTimes = 0;for (Map.Entry<Integer, Integer> entry : timesMap.entrySet()) {maxTimes = Math.max(entry.getValue(), maxTimes);}// 找到出现次数最多的值List<Integer> maxTimesValList = new ArrayList<>();for (Map.Entry<Integer, Integer> entry : timesMap.entrySet()) {if (entry.getValue() == maxTimes) {maxTimesValList.add(entry.getKey());}}// List 转 arrayint[] result = new int[maxTimesValList.size()];for (int i = 0; i < maxTimesValList.size(); i++) {result[i] = maxTimesValList.get(i);}return result;}/*** 遍历二叉树时统计值的次数* @param node*/private void order(TreeNode node, Map<Integer, Integer> timesMap) {if (node == null) {return;}timesMap.put(node.val, timesMap.getOrDefault(node.val, 0) + 1);order(node.left);order(node.right);}
优化算法,减少对 Map 的遍历次数:
int maxTimes = 0;Map<Integer, Integer> timesMap = new HashMap<>();public int[] findMode(TreeNode root) {order(root);// 找到出现次数最多的值List<Integer> maxTimesValList = new ArrayList<>();for (Map.Entry<Integer, Integer> entry : timesMap.entrySet()) {if (entry.getValue() == maxTimes) {maxTimesValList.add(entry.getKey());}}// List 转 arrayint[] result = new int[maxTimesValList.size()];for (int i = 0; i < maxTimesValList.size(); i++) {result[i] = maxTimesValList.get(i);}return result;}/*** 遍历二叉树时统计值的次数* @param node*/private void order(TreeNode node) {if (node == null) {return;}int times = timesMap.getOrDefault(node.val, 0).intValue() + 1;maxTimes = Math.max(maxTimes, times);timesMap.put(node.val, times);order(node.left);order(node.right);}
上述算法可以找出并返回二叉树(自然也支持二叉搜索树)中的所有众数。
但是题目中既然提到了二叉搜索树,那么二叉搜索树和普通二叉树有什么区别呢?
二叉搜索树的特点:
- 左子树的值都比根节点的值小
- 右子树的值都比根节点的值大
正因为二叉搜索树的特点,那么如果中序遍历二叉搜索树,其遍历结果将是一个有序数组!!!
public int[] findMode(TreeNode root) {List<Integer> valList = new ArrayList<>();inOrder(root, valList);}/*** 中序遍历二叉搜索树,得到升序的遍历结果* @param node*/private void inOrder(TreeNode node, List<Integer> valList) {if (node == null) {return;}inOrder(node.left, valList);valList.add(node.val);inOrder(node.right, valList);}
那怎么从一个升序的有序数组中获取到众数集合呢?
List<Integer> maxValList = new ArrayList<>();int maxCount = 0;int preVal = -1;int count = 0;for (Integer val : valList) {if (preVal == -1) {// 初始化为1count = 1;} else if (preVal == val) {// 值相等,次数加1count++;} else {// 值不相等,次数重置为1count = 1;}// 记录当前值用于下一次比较preVal = val;if (count == maxCount) {// 若次数等于最大次数,将当前值放入结果集maxValList.add(val);}if (count > maxCount) {// 若次数大于最大次数,清空结果集maxValList.clear();// 将当前值放入结果集maxValList.add(val);// 将当前次数作为新的最大次数maxCount = count;}}
完整地算法:
public int[] findMode(TreeNode root) {List<Integer> valList = new ArrayList<>();inOrder(root, valList);List<Integer> maxValList = new ArrayList<>();int maxCount = 0;int preVal = -1;int count = 0;for (Integer val : valList) {if (preVal == -1) {// 初始化为1count = 1;} else if (preVal == val) {// 值相等,次数加1count++;} else {// 值不相等,次数重置为1count = 1;}// 记录当前值用于下一次比较preVal = val;if (count == maxCount) {// 若次数等于最大次数,将当前值放入结果集maxValList.add(val);}if (count > maxCount) {// 若次数大于最大次数,清空结果集maxValList.clear();// 将当前值放入结果集maxValList.add(val);// 将当前次数作为新的最大次数maxCount = count;}}// List 转 arrayint[] result = new int[maxValList.size()];for (int i = 0; i < maxValList.size(); i++) {result[i] = maxValList.get(i);}return result;}private void inOrder(TreeNode node, List<Integer> valList) {if (node == null) {return;}inOrder(node.left, valList);valList.add(node.val);inOrder(node.right, valList);}
优化算法:
public int[] findMode(TreeNode root) {List<Integer> maxValList = new ArrayList<>();inOrder(root, maxValList);// List 转 arrayint[] result = new int[maxValList.size()];for (int i = 0; i < maxValList.size(); i++) {result[i] = maxValList.get(i);}return result;}/*** 中序遍历二叉搜索树,得到升序的遍历结果* @param node*/TreeNode pre = null;int count;int maxCount = 0;public void inOrder(TreeNode node, List<Integer> maxValList) {if (node == null) {return;}inOrder(node.left, maxValList);if (pre == null) {// 第一个节点count = 1;} else if (node.val == pre.val) {// 当前节点和上一个节点值相等count++;} else {// 当前节点和上一个节点值不相等count = 1;}// 记录当前值pre = node;// 当前值出现次数和最大次数相等if (count == maxCount) {// 放入当前值到目标集合maxValList.add(node.val);}if (count > maxCount) {// 出现新的最大次数// 清空集合maxValList.clear();// 放入当前值到目标集合maxValList.add(node.val);// 重置最大次数maxCount = count;}inOrder(node.right, maxValList);}
