目录
1 什么是优先队列
1.1 优先队列与堆的关系
1.2 如何定义优先队列
1.3 如何使用优先队列
1.4 如何设置排序规则
2 347. 前 K 个高频元素
2.1 第 2 步的具体实现
2.2 举例说明
2.3 完整代码
3 215. 数组中的第 K 个最大元素 - v2
菜鸟做题,语言是 C++
1 什么是优先队列
1.1 优先队列与堆的关系
优先队列:它是一种抽象数据类型,可以看作是一个队列或栈的变体,元素按照优先级的高低排列。在 C++ 中,优先队列通常使用堆来实现。
个人理解,优先队列是对堆的底层原理的封装,谁不用谁是傻子 (bushi)
1.2 如何定义优先队列
参考博客:c++ 优先队列(priority_queue)用法详解
定义如下:
priority_queue<Type, Container, Functional>
① Type 是指数据类型,即你要装什么类型的数据。
② Container 是指容器类型,即你用什么样的容器装数据。
Container 必须是用数组实现的容器,比如:vector、deque等等,不能用 list 。STL 里面默认用的是 vector 。
③ Functional 是指比较的方式,即你希望数据之间以什么样的规则来排序。
只有当数据类型比较复杂的时候才需要设置 Functional,比如你的数据是个键值对。针对基本的数据类型,不需要设置 Functional,默认是大根堆(即谁大谁排前面)。
举例说明
假设我要让优先队列装 int 型的数据,那么就有:
priority_queue<int, vector<int>> q;
如果我不想使用默认的大根堆排序方法,则有:
priority_queue<int, vector<int>, decltype(& cmp)> q(cmp);
其中 cmp 是你自定义的排序函数,返回值是 bool 类型。
如果我想让优先队列装键值对类型的数据,则有:
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(& cmp)> q(cmp);
不过就是把数据类型换了,基本结构都是一样的。
1.3 如何使用优先队列
- q.push(data) 插入元素
- q.pop() 弹出队首元素
- q.top() 获取队首元素
- q.size() 获取队列大小
- q.empty() 判断是否为空
以上就是优先队列的基本操作,可以看出它和栈、队列的操作是一样的,无痛学习。
1.4 如何设置排序规则
假设我要为键值对排序,因此对队列定义如下:
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(& cmp)> q(cmp);
其中,pair<int, int> 的第一个 int 是键,第二个 int 是值。要求 cmp 是一个函数指针(即函数的地址),decltype 是类型推导。
排序规则如下:
static bool cmp(pair<int, int> m, pair<int, int> n) {return m.second > n.second;
}
其中,m.second 和 n.second 分别代表键值对 m 和 n 的值。
Q:为什么要加 static?
A:因为不加会报错:
- “error: must explicitly qualify name of member function when taking its address”
- “错误:在取成员函数的地址时必须显式限定成员函数的名称”
为什么是 m.second > n.second?这样看起来不是谁的值更大,谁排前面吗?答:我也不知道啊。
2 347. 前 K 个高频元素
好不容易通过 215. 数组中的第 K 个最大元素 学会了堆排序,准备大展身手,结果运行超时。。
解题思路:
- 遍历 nums 并使用 hash 为其中的所有数字计数
- 遍历 hash 并使用 priority_queue 为其中的计数结果排序
2.1 第 2 步的具体实现
① 定义优先队列:
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(& cmp)> q(cmp);
为什么存储的数据类型是 pair<int, int>?因为我们要同时存储 “数字” 和它的 “个数”,否则后面把 “个数” 的顺序排出来了,却不知道这些 “个数” 对应的 “数字” 是哪些。
② 遍历 hash 并使用 priority_queue 为其中的计数结果排序:
- 依次向 priority_queue 插入数据,但限制 priority_queue 的大小最终为 k
- 若当前元素的 count 比队首元素的 count 大,那么弹出队首,换当前元素进去
for (auto & [num, count] : hash) {if (q.size() < k) {q.push(make_pair(num, count));} else if (q.size() == k) {if (count > q.top().second) {q.pop();q.push(make_pair(num, count));}}
}
2.2 举例说明
这里用字母只是为了方便区分,原题还是用的数字
- 假设 nums = [a, b, b, c, c, c], k = 2
- 易得 hash = [(a, 1), (b, 2), (c, 3)]
下面来看 priority_queue 是怎么操作的:
第 1 时刻,队列大小显然没有到达 k,因此直接插入键值对。第 2 时刻,队列大小显然也没有到达 k,因此直接插入键值对。第 3 时刻,由于席位已满,因此必须判断谁更配进入队列。由于 a 的个数被 c 的个数少,因此 a 被弹出队列为 c 让位。
注意:每当有新元素进入队列的时候,队列都会按照之前设置的排序规则 cmp 为元素重新排队。
2.3 完整代码
class Solution {
public:// 排序规则static bool cmp(pair<int, int> m, pair<int, int> n) {return m.second > n.second;}vector<int> topKFrequent(vector<int>& nums, int k) {// 计数unordered_map<int, int> hash;for (auto & n : nums) {hash[n]++;}// 排序priority_queue<pair<int, int>, vector<pair<int, int>>,decltype(& cmp)> q(cmp);for (auto & [num, count] : hash) {if (q.size() < k) {q.push(make_pair(num, count));} else if (q.size() == k) {if (count > q.top().second) {q.pop();q.push(make_pair(num, count));}}}// 处理结果vector<int> ans;while (!q.empty()) {ans.push_back(q.top().first);q.pop();}return ans;}
};
3 215. 数组中的第 K 个最大元素 - v2
模仿 347. 前 K 个高频元素 即可
class Solution {
public:static bool cmp(int a, int b) {return a > b;}int findKthLargest(vector<int>& nums, int k) {priority_queue<int, vector<int>, decltype(& cmp)> q(cmp);for (auto & n : nums) {if (q.size() < k) {q.push(n);} else if (q.size() == k) {if (n > q.top()) {q.pop();q.push(n);}}}return q.top();}
};
当你有了优先队列,谁还看堆排 (〃>皿<)