描述
1.优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
empty():检测容器是否为空
size():返回容器中有效元素个数
front():返回容器中第一个元素的引用
push_back():在容器尾部插入元素
pop_back():删除容器尾部元素
5. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定priority_queue类实例化指定容器类,则使用vector。
6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。
一、常见接口
二、模拟实现
优先级队列底层用二叉树堆实现的,所以队列实现起来跟堆大致相同,不过引入一个新的概念,仿函数-仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载 operator() 运算符。因为调用仿函数,实际上就是通过类对象调用重载后的 operator() 运算符。使用仿函数帮助我们更方便实现泛型模板。
//一个简单的仿函数
template<class T>
class Less
{
public:
//重载 '()'
bool operator()(const T& x,const T& y)
{
//内置类型直接比大小,自定义大小会调用他们自己的运算符重载 ‘<’ 比较大小
return x<y;
}
}
#include<iostream>
#pragma once
#include<vector>
namespace tzc
{
//此处的最后一个模板参数使用仿函数,默认调用库中的less 这样建堆默认是大堆
template<class T,class Container =vector<T>,class Compare =less<int>>
class priority_queue
{
private:
//向下调整
void AdjustDown(int parent)
{
Compare com;
//找出左右孩子大的
int child = parent * 2 + 1;
while (child < _con.size())
{
if (child + 1 < _con.size() && com(_con[child],_con[child + 1]))
{
++child;
}
if (com(_con[parent],_con[child]))
{
//孩子结点比父节点还大
swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else break;
}
}
//向上调整
void AdjustUp(int child)
{
Compare com;
int parent = (child - 1) / 2;
while (child > 0)
{
//如果孩子结点比父节点大交换
if (com(_con[parent],_con[child]))
{
swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
public:
priority_queue()
{
}
template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
while (first != last)
{
_con.push_back(*first);
++first;
}
//建堆
for (int i =(_con.size() - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(i);
}
}
void pop()
{
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
AdjustDown(0);
}
void push(const T& val)
{
_con.push_back(val);
AdjustUp(_con.size() - 1);
}
const T& top()
{
return _con[0];
}
bool empty()
{
return _con.empty();
}
int size()
{
return _con.size();
}
private:
Container _con;
};
void test_priority()
{
priority_queue<int> pq;
pq.push(3);
pq.push(5);
pq.push(1);
pq.push(4);
while (!pq.empty())
{
cout << pq.top()<<" ";
pq.pop();
}
cout << endl;
}
}
二、常见Oj面试题
数组中的第K个最大元素
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
实例1:
输入: [3,2,1,5,6,4], k = 2
输出: 5
实例2:
输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4
解:
方法①
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
//默认大堆 建大堆
priority_queue<int> pq(nums.begin(), nums.end());
//将比第k个大的元素pop出队
for (int i = 0; i < k-1; i++)
{
pq.pop();
}
//取队头元素即为第k个大元素
return pq.top();
}
};
方法②
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
//建小堆 建一个有k个元素的小堆
priority_queue<int,vector<int>,greater<int>> pq(nums.begin(),nums.begin()+k);
//堆顶元素即为k个元素中最小元素,将剩余元素入队,如果比队头元素大,则进队,
//将原先队头元素出队,这样可以保证队中元素永远只有k个,将所以元素以此方法进队后,队头元素即为第k个大的元素
for(int i=k;i<nums.size();i++)
{
if(nums[i]>pq.top())
{
pq.pop();
pq.push(nums[i]);
}
}
return pq.top();
}
};