前 K 个高频元素 - LeetCode 热题 75

大家好!我是曾续缘😛

今天是《LeetCode 热题 100》系列

发车第 75 天

堆第 2 题

❤️点赞 👍 收藏 ⭐再看,养成习惯

前 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 是数组大小。

难度:💖💖

解题方法

这道题目要求我们找出数组中出现频率前k高的元素。为了有效地解决这个问题,我们可以使用一个哈希表来记录每个元素出现的频率,然后使用一个大小为k的最小堆来帮助我们获取前k个高频元素。

步骤1:统计元素频率

我们首先遍历整个数组,使用一个哈希表(在这里使用HashMap)来记录每个元素及其出现的次数。

Map<Integer, Integer> mp = new HashMap<Integer, Integer>();
for(int num : nums){
    mp.put(num, mp.getOrDefault(num, 0) + 1);
}

这段代码中,mp用于存储每个数字及其出现的次数。

步骤2:使用最小堆维护前k个高频元素

接下来,我们使用一个最小堆(这里用PriorityQueue实现)来存储出现频率最高的k个元素。堆中的每个元素是一个长度为2的数组,第一个元素是数字本身,第二个元素是该数字的出现次数。

PriorityQueue<int[]> q = new PriorityQueue<int[]>(new Comparator<int[]>(){
    public int compare(int[] m, int[] n){
        return m[1] - n[1];
    }
});

这个堆会根据第二个元素(即频率)来排序,保证每次弹出的都是频率最高的元素。

步骤3:填充最小堆

我们再次遍历哈希表中的所有元素,将每个元素添加到最小堆中,如果堆的大小超过了k,那么我们就比较当前元素的频率和堆顶元素的频率,如果当前元素的频率更高,我们就将堆顶元素弹出,然后将当前元素加入堆中。

for(Map.Entry<Integer, Integer> entry : mp.entrySet()){
    int num = entry.getKey(), cnt = entry.getValue();
    if(q.size() == k){
        if(q.peek()[1] < cnt){
            q.poll();
            q.offer(new int[]{num, cnt});
        }
    }else{
        q.offer(new int[]{num, cnt});
    }
}

这段代码确保了最小堆中始终保持了频率最高的k个元素。

步骤4:提取结果

最后,当哈希表遍历完毕后,最小堆中就包含了k个高频元素。我们只需要将堆中的元素按顺序取出,就得到了最终的结果。

int[] ans = new int[k];
for(int i = 0; i < k; i++){
    ans[i] = q.poll()[0];
}

这段代码将堆中的元素(除去频率,只保留数字本身)存放到结果数组ans中,并返回这个数组。

Code

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> mp = new HashMap<Integer, Integer>();
        for(int num : nums){
            mp.put(num, mp.getOrDefault(num, 0) + 1);
        }
        PriorityQueue<int[]> q = new PriorityQueue<int[]>(new Comparator<int[]>(){
            public int compare(int[] m, int[] n){
                return m[1] - n[1];
            }
        });
        for(Map.Entry<Integer, Integer> entry : mp.entrySet()){
            int num = entry.getKey(), cnt = entry.getValue();
            if(q.size() == k){
                if(q.peek()[1] < cnt){
                    q.poll();
                    q.offer(new int[]{num, cnt});
                }
            }else{
                q.offer(new int[]{num, cnt});
            }
        }
        int[] ans = new int[k];
        for(int i = 0; i < k; i++){
            ans[i] = q.poll()[0];
        }
        return ans;
    }
}
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值