每日一题:前K个高频元素

74 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情

力扣题目链接

给你一个整数数组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个高频元素的集合是唯一的

解题思路:最小堆

这道题的思路很好写

  1. 统计元素和元素在数组中出现的次数
  2. 排序
  3. 找出前k个高频元素

我们可以使用map统计元素出现的次数,mapkey用来存放元素的数值,value用来存放元 素在数组中出现的次数

但是到了第二步,对map排序,这块的代码就不好写了,我是考虑过用快排来排序的,但使用快排要将map转换为vector的结构,然后对整个数组进行排序,复杂度太高了

在看其他人写的题解过程中,我发现可以使用一种叫优先级队列的容器适配器来进行排序

优先级队列是什么呢,其实就是披着队列皮的,尽管它叫做优先级队列,但堆并不是队列

因为队列中允许的操作是先进先出(FIFO),在队尾插入元素,在队头取出元素。

而堆虽然在堆底插入元素,在堆顶取出元素,但是堆中元素的排列不是按照到来的先后顺序,而是按照一定的优先顺序排列的。

那么堆又是什么呢,堆就是一个完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。  

如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。

从小到大排就是小顶堆,从大到小排就是大顶堆。

那么本题是要使用最小堆呢还是最大堆呢?

答案是使用最小堆,我刚开始看到的时候也有点想不通,题目要求我们返回高频元素,按理说应该选择从大到小的大顶堆啊,怎么会使用小顶堆呢。随着我往下看,才明白了要使用小顶堆的原因

使用最小堆的原因

如果使用大顶堆,那在每次移动更新大顶堆的时候,每次弹出都把最大的元素弹出去了,那么怎么保留下来前K个高频元素呢。

所以我们要用小顶堆,要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。

具体代码:(JAVA实现)

public static int[] topKFrequent(int[] nums,int k ){

    //用mao来存放元素出现频率
    HashMap<Integer, Integer> hashMap = new HashMap<>();

    //遍历过程
    for (int i = 0; i < nums.length; i++) {
        if (hashMap.containsKey(nums[i])) {
            hashMap.put(nums[i],hashMap.get(nums[i]) + 1);
        }else {
            hashMap.put(nums[i],1);
        }
    }

    //遍历map,用最小堆保存频率最大的k个元素
    PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return hashMap.get(o1) - hashMap.get(o2);
        }
    });

    //用固定大小为k的小顶堆,扫面所有频率的数值
    for (Integer key : hashMap.keySet()) {
        if (queue.size() < k) {
            queue.add(key);
        }else if (hashMap.get(key) > hashMap.get(queue.peek())) {
            queue.remove();
            queue.add(key);
        }
    }

    //找出前K个高频元素,将最小堆里的元素放进新定义的数组里
    int[] ints = new int[k];
    int count = 0;
    while (!queue.isEmpty()) {
        ints[count++] = queue.poll();
    }

    return ints;
}

复杂度分析

  • 时间复杂度:O(n log k)
  • 空间复杂度:O(n)n为哈希表长度

提交结果

image.png