2025年- H65-Lc173--347.前k个高频元素(小根堆,堆顶元素是当前堆元素里面最小的)--Java版

1.题目描述

2.思路


(1)这里定义了一个小根堆(最小堆),根据元素的频率从小到大排序。小根堆原理:堆顶是最小值,每次插入或删除操作会保持堆的有序结构(常用二叉堆实现)。

比如,map.get(a) - map.get(b) 表示:

频率小的排在堆顶;

当堆满 k 个元素后,就可以比较新元素与堆顶的大小,进行替换。

(2)这个循环遍历 map 中的所有 key,目的是找出频率前 k 高的元素。

如果堆还没满,直接加入;

如果堆满了,比较当前元素的频率和堆顶元素频率:

当前元素更高,则替换堆顶。

继续例子(k = 2)

map: {1=3, 2=2, 3=1}

按顺序加入:

加入 1,堆 = [1]

加入 2,堆 = [1, 2](根据频率排序,2 的频率低,堆顶是 2)

处理 3(频率 1):

频率小于堆顶 2 → 不加入

最终堆中是 [1, 2]

🧩 举个例子来直观理解:

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

(2)频率统计结果:map = {1=3, 2=2, 3=1}

维护一个小根堆 pq,大小最多为 2(k=2):

加入 1(频率3)→ pq = [1]

加入 2(频率2)→ pq = [2, 1](按频率排序,堆顶是频率最小的)

尝试加入 3(频率1):

频率 1 < 堆顶 2 的频率 ⇒ 不加入

现在 pq 中是 [2, 1],是频率前 2 高的元素!

取出堆中元素:

int[] result = new int[k];

for (int i = 0; i < k; i++) {

result[i] = pq.remove(); // remove 堆顶元素

}

return result;

虽然 remove() 是按频率从小到大弹出,但我们关心的是内容是这 k 个元素本身是否是频率最高的

它不保证从大到小的顺序(如果你想要排好顺序,可以再排序)

总结这段逻辑:

小根堆始终维护的是「当前频率最高的前 k 个元素」;

取出这些元素即可完成任务;

堆顶是这 k 个元素中最小的,所以我们才用小根堆。

3.代码实现

java 复制代码
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值
          HashMap<Integer, Integer> map = new HashMap<>();
          for (int num : nums) 
        {
            if(map.containsKey(num))
            {
                //如果字典元素在后续遍历的过程中又再次出现,直接次数+1
                 map.put(num, map.get(num) + 1);
            }else
            {
                //如果该字典元素是首次出现。
                map.put(num, 1);
            }
        }
        //遍历map,用最小堆保存频率最大的k个元素
        //优先级队列的底层原理就是堆
       PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
            public int compare(Integer a, Integer b) {
                return map.get(a) - map.get(b);  // 出现次数小的排前面(小根堆)
            }
        });
         // 遍历 map 的 key 值
        for (Integer key : map.keySet()) {
            if (pq.size() < k) {
                pq.add(key);  // 队列还未满,直接加入
            } else if (map.get(key) > map.get(pq.peek())) {
                pq.remove();      // 弹出堆顶元素(频率最小)
                pq.add(key);      // 加入当前频率更高的元素
            }
        }

        //取出最小堆的元素
       int[] result=new int[k];
       for(int i=0;i<k;i++)
       {
        result[i]=pq.remove();
       }
       return result;
       //pq.remove() 会返回堆顶元素,并从堆中删除该元素;
    }
}
相关推荐
User_芊芊君子3 分钟前
【Java】设计模式——单例、工厂、代理模式
java·设计模式·代理模式
六点半88811 分钟前
【C++】C++11 篇二
开发语言·c++
2301_8035545212 分钟前
正向代理,反向代理,负载均衡还有nginx
java·nginx·负载均衡
要开心吖ZSH14 分钟前
软件设计师备考-(十六)数据结构及算法应用(重要)
java·数据结构·算法·软考·软件设计师
DDDDDDDRDDR18 分钟前
C++容器:list
开发语言·c++·stl
向上的车轮22 分钟前
基于Java Spring Boot的云原生TodoList Demo 项目,验证云原生核心特性
java·spring boot·云原生
Elnaij23 分钟前
从C++开始的编程生活(7)——取地址运算符重载、类型转换、static成员和友元
开发语言·c++
程序员清风24 分钟前
快手一面:为什么要求用Static来修饰ThreadLocal变量?
java·后端·面试
逍遥德25 分钟前
Java8 Comparator接口 和 List Steam 排序使用案例
java·spring boot·list·排序算法
chen_ever34 分钟前
golang之go modules
开发语言·后端·golang