优先级队列
- 基本使用
- 题目训练
基本使用
priority_queue
, 优先级队列, 又叫做双端队列, 头文件也是 <queue>
别看它叫做队列, 其实它是一个 堆
补充一下概念:
- 大根堆 — — 每一棵树的父节点比它的孩子都大
- 小跟堆 — — 每一棵树的父节点比它的孩子都小
👇👇👇
void test()
{
// 默认构建的是一个大堆
priority_queue<int> pq;
pq.push(1);
pq.push(5);
pq.push(4);
pq.push(9);
pq.push(10);
pq.push(6);
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}
}
int main()
{
test();
return 0;
}
运行结果:
10 9 6 5 4 1
注意:
不要被这里的按序输出迷惑了,
优先级队列的内部结构是堆, 堆的内部结构是不一定按照元素的顺序排列的。
- 那如果我们要
小跟堆输出
呢?
void test()
{
// 仿函数控制, greater是构建小跟堆
priority_queue<int,vector<int>, greater<int> > pq;
pq.push(1);
pq.push(6);
pq.push(20);
pq.push(15);
pq.push(8);
pq.push(2);
pq.push(6);
pq.push(4);
pq.push(9);
pq.push(10);
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}
}
int main()
{
test();
return 0;
}
运行结果:
1 2 4 6 6 8 9 10 15 20
- 要构建小跟堆, 我们要用
greater
- 由于仿函数是最后一个参数, 那么处于中间位置的
容器适配器
也要给个参数
如何使用
- 使用迭代区间初始化
void test()
{
vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(9);
vec.push_back(8);
vec.push_back(7);
priority_queue<int> pq(vec.begin(), vec.end());
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}
cout << endl;
}
int main()
{
test();
return 0;
}
运行结果:
9 8 7 2 1
当然我们也可以 建小堆
void test()
{
vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(9);
vec.push_back(8);
vec.push_back(7);
priority_queue<int,vector<int>, greater<int> > pq(vec.begin(), vec.end());
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}
cout << endl;
}
int main()
{
test();
return 0;
}
运行结果:
1 2 7 8 9
题目训练
- 数组中的第K个最大元素
这是我们熟悉的 TopK问题
.
那么问题来了, 要求时间复杂度是 O(n)
, 我们是建小堆还是大堆呢?
其实, 建小堆 和 建大堆都是一样的
- 建大堆
- 建大堆 — — 时间复杂度是
O(n)
- pop (k-1) 次 — — 向上调整算法, 时间复杂度是
O(logN)
- 堆顶元素就是答案
- 建大堆 — — 时间复杂度是
总体的时间复杂度是 O(n)
class Solution {
public:
int findKthLargest(vector<int>& nums, int k)
{
// 1. 建大堆
// 建堆 -- O(n)
priority_queue<int> pq(nums.begin(), nums.end());
// 2. pop(k-1)次
// pop, 向上调整算法 -- O(logN)
while(--k)
{
pq.pop();
}
return pq.top();
}
};
- 建小堆
- 建一个
个数为K的小堆
— — 时间复杂度是O(K)
- 将剩余元素中的
大于堆顶元素的数据 插入堆中
— — 时间复杂度是(N-K)l ogK
1. 如果 K很大, 那么时间复杂度是O(log K)
2. 如果 K很小, 那么时间复杂度是O(N)
- 返回堆顶元素
- 建一个
总体的时间复杂度是 O(n)
class Solution {
public:
int findKthLargest(vector<int>& nums, int k)
{
// 1. 建小堆
// 建堆 -- O(n)
priority_queue<int, vector<int>, greater<int> > pq(nums.begin(), nums.begin()+k);
// 2. pop
// pop, 向上调整算法 -- O((N-K) logK)
// push, 向上调整算法 -- O((N-K) logK)
for(int i = k; i < nums.size(); i++)
{
if(nums[i] > pq.top())
{
pq.pop();
pq.push(nums[i]);
}
}
return pq.top();
}
};
弟子曰:此学甚好,只是簿书讼狱繁难,不得为学。
阳明曰:簿书讼狱之间,无非实学;若离了事物为学,却是着空。
译文:
弟子说:先生的学问很好,但是平时工作太忙了,没空修习。
先生说:学问不能脱离工作,在工作中修行,才是真正的学习。如果脱离了实际事务,那修行就没用了。
王阳明认为:工作就是最好的修行。
把工作中遇到的烦恼,当成磨炼自己心性的机会;
把工作中的怠惰懒散,当成改变自己态度的试炼;
把工作中的愤怒委屈,当成放大格局的磨砺。
稻盛和夫说:工作中修行,是帮助我们提升心性和培养人格的最重要、也是最有效的方法。
工作是用,修身是体 ,用成长的心态去工作,就是一个人最好的修行。