刷刷刷 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);
    }
}
重难点

重点还是要掌握二叉搜索树的特性,及使用中序遍历能输出的是个有序结果。同时这个题目也比较特殊的是有相等的元素。

附:学习资料链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值