LeetCode-347. 前 K 个高频元素

题目来源

347. 前 K 个高频元素

题目详情

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

提示:

  • 1 <= nums.length <= 105
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

进阶: 你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n是数组大小。

题目解析

解法一:快速排序

  1. 从本题的题目就可以看出,这是一个top-k问题,遇到这个问题就应该首先想到快速排序。
  2. 与传统快速排序不同的是,这里要求解的是出现最高评率的top-k个数,所以这里可以额外构建一个类或者结构体来存放二元值。
  3. 此外,还需要注意的问题就是快速排序代码的编写,有一些边界情况需要注意。
class Solution {
    class NumFre{
        int num;
        int frq;
        NumFre(){}
        NumFre(int _num, int _frq){
            num = _num;
            frq = _frq;
        }
    }
    public int[] topKFrequent(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();
        int n = nums.length;
        for(int i=0; i<n; i++){
            int num = nums[i];
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        NumFre fre[] = new NumFre[map.size()];
        int i = 0;
        for(Map.Entry<Integer, Integer> entry : map.entrySet()){
            fre[i++] = new NumFre(entry.getKey(), entry.getValue());
        }
        quicksort(fre, 0, fre.length -1, k);
        int[] res = new int[k];
        for(i=0; i<k; i++){
            res[i] = fre[i].num;
        }
        return res;
    }

    private void quicksort(NumFre[] nums, int left, int right, int k){
        if(left > right)
            return;
        int mid = partition(nums, left, right);
        if(mid == k-1){
            return;
        }
        if(mid > k){
            quicksort(nums, left, mid-1, k);
        }else{
            quicksort(nums, mid+1, right, k);
        }
    }
    private int partition(NumFre[] nums, int left, int right){
        NumFre base =  new NumFre(nums[left].num, nums[left].frq);
        while(left < right){
            while(left < right && nums[right].frq <= base.frq){
                right--;
            }
            if(right > left){
                copyObjectArrayEle(nums[left] , nums[right]);
                left++;// 容易忽略
            }
            while(left < right && nums[left].frq >= base.frq){
                left++;
            }
            if(left < right){
                copyObjectArrayEle(nums[right], nums[left]);
                right--;
            }
        }
        copyObjectArrayEle(nums[left] , base);
        return left;
    }
    private void copyObjectArrayEle(NumFre numsi, NumFre numsj){
        numsi.num = numsj.num;
        numsi.frq = numsj.frq;
    }
}

解法二:堆排序

  1. top-k的另一个更加直观的解法是使用堆排序。
  2. 利用java里面的优先队列维护一个小根堆,保证优先队列里的始终是前k大的数。
class Solution {
    class NumFre{
        int num;
        int frq;
        NumFre(){}
        NumFre(int _num, int _frq){
            num = _num;
            frq = _frq;
        }
    }
    public int[] topKFrequent(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();
        int n = nums.length;
        for(int i=0; i<n; i++){
            int num = nums[i];
            map.put(num, map.getOrDefault(num, 0) + 1);
        }

        PriorityQueue<NumFre> prique = new PriorityQueue<>((o1, o2) ->{
            return o1.frq - o2.frq;// 构造小顶堆
        });

        for(Map.Entry<Integer, Integer> entry : map.entrySet()){
            NumFre fre = new NumFre(entry.getKey(), entry.getValue());
            int size = prique.size();
            if(size < k){
                prique.offer(fre);
            }else if(size == k){
                if(fre.frq > prique.peek().frq){
                    prique.poll();
                    prique.offer(fre);
                }
            }
        }
        int[] res = new int[k];
        int i=0;
        while(!prique.isEmpty()){
            NumFre fre = prique.poll();
            res[i++] = fre.num;
        }
        return res;
    }
}
posted @ 2021-12-26 10:33  Garrett_Wale  阅读(87)  评论(0编辑  收藏  举报