LeetCode347. 前 K 个高频元素

 

 

 

 题解分析:首先求出所有元素的频次(映射Map),然后利用频次求出前k高的元素(优先队列)。具体来说,问题转化为“在N个元素中选出前M个元素,M<<N”,最朴素的想法是采用排序,但使用快排等高级排序算法,复杂度在NlogN;更好的方法是采用优先队列,复杂度在NlogM,其思路是使用优先队列,维护当前看到的前M个元素,不断将前M大元素中最小的元素进行替换,因此需要使用最小堆。

用Java标准库中的优先队列PriorityQueue内部默认是一个最小堆。如果想要改变Java标准库中的类相应的比较方式,解决方案是定义一个新的类(比较器,自定义在优先队列中优先级的比较方式),它实现的是Comparator这个接口,在优先队列构造时,让这个比较器作为优先队列的一个参数,比较器定义了在优先队列中如何决定谁的优先级大。

再进一步,不需要专门为PriorityQueue设置一个类,将一个只用一次的类的声明写成匿名类,即传的参数设为一个匿名类,然后把compare的逻辑写出来即可。

更进一步,匿名类具有变量捕获的能力,匿名类中能拿到算法中声明的所有不可改变的变量,利用匿名类改变Java内置类型之间比较的逻辑。

还能进一步化简,从Java8开始,匿名类可以直接使用lamda表达式。

 

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        /**
         *  思路:维护优先队列。
         *      时间复杂度O(nlogK) 每次堆操作需要O(logK)的时间
         *      空间复杂度O(n)
         */
        Map<Integer,Integer> map = new HashMap<>();
        for (int num : nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        // 求前 k 大,用小根堆,求前 k 小,用大根堆。
        PriorityQueue<Integer> minHeap = new PriorityQueue<>((o1, o2) -> map.get(o1) - map.get(o2));
        for (int key : map.keySet()) {
            if (minHeap.size() < k) {
                minHeap.add(key);
            }else {
                if (map.get(minHeap.peek()) < map.get(key)) {
                    minHeap.poll();
                    minHeap.add(key);
                }
            }
        }
        int[] res = new int[k];
        int index = 0;
        for (int n : minHeap) {
            res[index ++] = n;
        }
        return res;
    }
}

 

posted @ 2020-05-05 23:36  不学无墅_NKer  阅读(209)  评论(0编辑  收藏  举报