首页 > 其他分享 >刷刷刷 Day 21 | 501. 二叉搜索树中的众数

刷刷刷 Day 21 | 501. 二叉搜索树中的众数

时间:2023-01-25 17:13:27浏览次数:57  
标签:node count pre 21 map int res 众数 树中

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

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

附:学习资料链接

标签:node,count,pre,21,map,int,res,众数,树中
From: https://www.cnblogs.com/blacksonny/p/17067066.html

相关文章