刷刷刷 Day 21 | 501. 二叉搜索树中的众数
501. 二叉搜索树中的众数
LeetCode题目要求
给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义:
结点左子树中所含节点的值 小于等于 当前节点的值
结点右子树中所含节点的值 大于等于 当前节点的值
左子树和右子树都是二叉搜索树

示例
输入:root = [1,null,2,2] 输出:[2]
解题思路
在二叉搜索树中,我们要找到出现频率最高的元素,那么根据二叉搜索树的特性,我们首先可以想到的是像数组一样,统计每个元素出现的个数。
那么最简单的方法是遍历二叉搜索树,并通过 Map 记录元素及元素出现的次数;然后最元素出现的次数降序排序;最后遍历 Map.values,由于它已经是降序排序过的,那么通过第一个元素出现的次数与后面的对比,就把所有最高频率出现的元素找到了。 以下就是代码实现:
// 暴力方式 public int[] findMode1(TreeNode root) { // 出现频率最高的元素, 数量最大的元素 // 存在相同的数,找到最大相同的元素,如果存在多个也要输出 // 不存在相同数,那么就是把所有的元素直接删除,因为它们的数量都是1,也是最高频率 if (root.left == null && root.right == null) { return new int[] {root.val}; } List<Integer> res = new ArrayList<>(); // 怎么知道数量不相等,怎么知道数量都相等呢,用 map 存储数值及出现的频率,频率排序 Map<Integer,Integer> map = new HashMap<>(); traversal1(root, map); // 对 map 值排序 // List<Map.Entry<Integer, Integer>> entryList = map.entrySet().stream() // .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) // .collect(Collectors.toList()); List<Map.Entry<Integer, Integer>> entryList = map.entrySet().stream() .sorted((c1, c2) -> c2.getValue().compareTo(c1.getValue())) .collect(Collectors.toList()); res.add(entryList.get(0).getKey()); for (int i = 1; i < entryList.size(); i++) { if (entryList.get(i).getValue() == entryList.get(i-1).getValue()) { res.add(entryList.get(i).getKey()); } else { break; } } int[] result = new int[res.size()]; for (int i = 0; i < res.size(); i++) { result[i] = res.get(i); } return result; // return res.stream().mapToInt(Integer::intValue).toArray(); } private void traversal1(TreeNode node, Map<Integer,Integer> map) { // 中序遍历, 左中右 if (node == null) { return; } traversal1(node.left, map); map.put(node.val, map.getOrDefault(node.val, 0) + 1); traversal1(node.right, map); }
然而上面的方法我们在遍历二叉树的时候,用了额外的Map、元素排序等操作,使得处理效率比较低。
其实可以通过类似双指针的方式,使用 pre 节点,count,maxCount 几个变量对于元素的记录,完成众数的记录。具体参见代码及详细注释
递归迭代,上代码
class Solution { // 记录每个元素出现的次数 private int count; // 记录每个元素出现的最大次数 private int maxCount; // 临时存储前一个节点 private TreeNode pre; // 结果集 private List<Integer> res; public int[] findMode(TreeNode root) { // 初始化 List res = new ArrayList<>(); // 调用 递归方法,获取众数 traversal(root); // 遍历结果 List,转换成 数组 int[] result = new int[res.size()]; for (int i = 0; i < res.size(); i++) { result[i] = res.get(i); } return result; } // 递归,一次取结果 private void traversal(TreeNode node) { // 递归终止条件,当节点为空时就返回 if (node == null) { return; } // 采用中序遍历 左 --> 中 --> 右 , 输出的是个有序结果 // 左,递归左节点 traversal(node.left); // 中 节点处理,这里会对元素及值进行处理 // 如果是第一次递归,那么 pre 为 null ,此时 node count 计数为1;同时如果 pre 不为 null 时且与 node 的值不相等,那也要 count 计数为 1 if (pre == null || pre.val != node.val) { count = 1; } else if (pre.val == node.val) { // 因为是有序结果,那么前一节点 pre 的值可能和当前 node 节点的值相等,那么就将 count 数量 + 1 count++; } // 移动 pre,遍历过程中,相当于指针的移动, pre 要向后移动了 pre = node; // 经过上面的操作后,count 可能已经出现众数结果了,如果已经达到 maxCount 就要输出结果, // 但这里的疑惑点在于 maxCount 在开始还是初始值,但是没问题,因为众数频率可能都是 1 呢。后面还会对他们进行处理 if (count == maxCount) { res.add(node.val); } // 上一步的处理结果虽然将结果放入结果集,但是经过递归后 count 有了变化,可能会大于之前的 maxCount, // 当出现了新的最大值,就要清空结果集,重新将众数放入结果集 if (count > maxCount) { maxCount = count; res.clear(); res.add(node.val); } // 右节点递归 traversal(node.right); } }
重难点
重点还是要掌握二叉搜索树的特性,及使用中序遍历能输出的是个有序结果。同时这个题目也比较特殊的是有相等的元素。
附:学习资料链接
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了