前 K 个高频元素

cpp 复制代码
class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> mp; // key: 数字, value: 出现频率
        // 默认是大顶堆,存储 pair<频率, 数字>
        // pair 比较时先看第一个元素(频率),再看第二个元素(数字)
        priority_queue<pair<int, int>> pq; 
        vector<int> res;

        // 1. 统计频率:遍历一遍数组 O(N)
        for(int num : nums) {
            mp[num]++;
        }

        // 2. 将频率和数字压入大顶堆:O(M log M),M 是去重后的数字个数
        for(auto it = mp.begin(); it != mp.end(); it++) {
            // 注意:这里把 it->second(频率)放在前面,方便堆按频率排序
            pq.push(pair<int, int>(it->second, it->first));
        }

        // 3. 弹出前 k 个频率最高的数字:O(K log M)
        while(k--) {
            res.push_back(pq.top().second); // 取出数字部分
            pq.pop();
        }

        return res;
    }
};

2. 运行步骤模拟

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

第一步:哈希表计数

  • mp = {1: 3, 2: 2, 3: 1} (数字 1 出现了 3 次,依此类推)

第二步:压入大顶堆 堆会根据 pair 的第一个元素(频率)自动调整,此时堆的内容(逻辑结构)如下:

  1. (3, 1) <- 堆顶(频率最高)

  2. (2, 2)

  3. (1, 3)

第三步:取出结果

  1. 第一次 pop:取回 1(频率为 3),res = [1]

  2. 第二次 pop:取回 2(频率为 2),res = [1, 2]

  3. k 减到 0,循环结束。

最终结果[1, 2]

在 C++ STL 中,priority_queue(优先队列)是一个非常实用的容器适配器。

简单来说,它就像是一个**"自带排队规则"的队列**。普通的队列(queue)是先进先出(FIFO),而优先队列是**"最优先的先出"**。


1. 核心特性:谁强谁先出

priority_queue 中,元素并不是按照进入的先后顺序排列的,而是按照优先级排列的。

  • 默认行为(大顶堆):默认情况下,优先级最高的是"数值最大"的元素。即使你是最后一个进去的,只要你最大,你也会排在最前面。

  • 底层实现 :它的底层通常是用 二叉堆 (Binary Heap) 实现的。这保证了插入元素和删除堆顶元素的时间复杂度都是 O(\\log N)


2. 基本操作

它的接口和普通的 stackqueue 非常相似:

操作 函数 说明 时间复杂度
入队 pq.push(val) 将元素插入队列并自动调整位置 O(\\log N)
出队 pq.pop() 删除优先级最高的元素(堆顶) O(\\log N)
查顶 pq.top() 返回优先级最高的元素(不删除) O(1)
判空 pq.empty() 检查队列是否为空 O(1)

3. 三种常见的定义方式

面试中经常需要根据需求改变排序规则:

A. 默认:大顶堆(大的先出)

C++

复制代码
// 默认把最大的整数放在堆顶
priority_queue<int> pq; 
B. 小顶堆(小的先出)

如果你想找"前 K 个最小"或者让小的数字先出来:

C++

复制代码
// 需要三个参数:元素类型,底层容器,比较函数
priority_queue<int, vector<int>, greater<int>> pq; 
C. 自定义比较(处理结构体或 Pair)

就像你之前写的 pair<频率, 数字>

C++

复制代码
// 存储 pair,默认先比较 pair.first,再比较 pair.second
priority_queue<pair<int, int>> pq; 

4. 形象理解:急诊室排队

  • 普通队列 (queue):银行办业务。谁先来谁先办,即便你只是存 100 块,也要等前面取 100 万的人办完。

  • 优先队列 (priority_queue):医院急诊室。医生不看谁先来,而是看谁的伤势最重(优先级最高)。伤势最重的人(比如大顶堆里的最大值)永远第一个就诊。


5. 为什么面试爱考它?

因为它在处理 "最" 字问题时极其高效:

  • 找数组中大的 K 个数。

  • 合并 K 个有序链表(每次取 K 个节点里小的)。

  • 迪杰斯特拉(Dijkstra)最短路径算法(每次取距离短的点)。

既然你已经知道 priority_queue 是基于"堆"实现的,你想看看它是如何通过"上浮"和"下沉"操作来维持这个顺序的吗?(这能帮你更好地理解 O(\\log N) 是怎么来的)

相关推荐
吴可可12313 分钟前
CAD2004自定义实体开发环境配置
c++·算法
装不满的克莱因瓶13 分钟前
矩阵的主成分是什么?主成分分析(PCA)又能做什么?
人工智能·线性代数·算法·机器学习·ai·矩阵·pca
大菜菜小个子18 分钟前
template<typename T>使用
java·开发语言·算法
Fanfanaas30 分钟前
C++ 继承
java·开发语言·jvm·c++·学习·算法
lqqjuly32 分钟前
模型合并与融合:理论、算法与可运行实现—从损失曲面几何到多模型融合
算法
memcpy042 分钟前
LeetCode 2144. 打折购买糖果的最小开销【贪心】
算法·leetcode·职场和发展
ID_180079054731 小时前
淘宝商品详情数据接口深度解析:架构、鉴权、数据结构与实战
数据结构·架构
散峰而望2 小时前
【算法练习】算法练习精选:陶陶摘苹果(基础+升级)、Music Notes、字串变换,你能AC几道?
数据结构·c++·算法·leetcode·贪心算法·github·动态规划
暗夜猎手-大魔王2 小时前
转载--Hermes Agent 04 | Agent 主循环:一次对话背后发生了什么
人工智能·python·算法
手写码匠2 小时前
华为云Flexus+DeepSeek征文|基于华为云Flexus X实例 + Dify + DeepSeek 构建企业级智能知识库问答系统实战
人工智能·深度学习·算法·aigc