priority_queue
1 接口使用
优先级队列priority_queue同样是个容器适配器,不同于stack和queue只是对容器的简单封装。它有三个模板参数:
template <class T, /* 数据类型 */
class Container = vector<T>, /* 适配容器 */
class Compare = less<typename Container::value_type> > /* 仿函数 */
class priority_queue;
priority_queue要求底层容器必须具有随机访问迭代器,支持empty
,size
,front
,push_back
,pop_back
几种接口,一般使用vector作底层容器。
priority_queue就是堆,能够实现堆的各种算法。因为要维护容器本身的特性,所以不支持遍历。
class Compare = less<typename Container::value_type>
priority_queue默认数值大优先级高,也就是默认大堆。想要排成小堆,需要指定priority_queue的仿函数参数,传入greator<T>
。
接口声明 | 解释 |
---|---|
priority_queue (Compare& cmp = Compare(), Container& ctnr = Container()) | 构造函数 |
bool empty() const | 判空 |
size_type size() const | 元素个数 |
value_type& top() | 栈顶元素 |
void push (const value_type& val) | 尾插 |
void pop() | 尾删 |
class Solution {
public:
//建大堆会选出整个数组的最大值,减小堆才能选出第k大的数
int findKthLargest(vector<int>& nums, int k) {
vector<int>::iterator pos = nums.begin() + k;
//k个数的小堆
priority_queue<int, vector<int>, greater<int>> pq(nums.begin(), nums.begin() + k);
while (pos != nums.end()) {
if (*pos > pq.top()) {
pq.pop();
pq.push(*pos);
}
++pos;
}
return pq.top();
}
};
2 模拟实现
基本接口
template<class T, class Container = std::vector<T>, class Compare = less<T>>
class priority_queue
{
public:
void push(const T& x) {
_con.push_back(x);
adjust_up(_con.size() - 1); //向上调整
}
void pop() {
swap(_con[0], _con[size() - 1]);
_con.pop_back();
adjust_down(0); //向下调整
}
bool empty() const {
return _con.empty();
}
int size() const {
return _con.size();
}
T& top() const {
return _con.front();
}
private:
Container _con;
Compare _cmp;
};
向上调整算法
void adjust_up(int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
//比较父节点与子节点的大小,并进行交换
if (_cmp(_con[parent], _con[child]))
swap(_con[parent], _con[child]);
else
break;
child = parent;
parent = (child - 1) / 2;
}
}
向下调整算法
void adjust_down(int parent)
{
int child = parent * 2 + 1;
while (child < _con.size())
{
//找出子节点的大一个
if (child + 1 < _con.size() && _cmp(_con[child], _con[child + 1]))
child++;
//比较父节点与子节点的大小,并进行交换
if (_cmp(_con[parent], _con[child]))
swap(_con[parent], _con[child]);
else
break;
parent = child;
child = parent * 2 + 1;
}
}
堆插入就是数组尾插一个元素,然后向上调整。
此时堆的性质可能被破坏,不过只会影响该结点到根结点所在路径上的所有结点,故顺势向上调整:一直交换结点数值直到满足堆的性质即可。
堆删除就是将尾元素覆盖到堆顶,然后向下调整。
只是堆顶元素不满足性质,其左右子树还是原样。只需将堆顶元素逐步向下调整:将根结点与其较大/小的子结点交换,只要父结点比子结点中任意一个大/小,就进行交换,直到交换到叶结点或不满足条件为止。
仿函数
决定大堆还是小堆,在于向上/向下调整算法中的父子节点的比较关系:
if (_cmp(_con[parent], _con[child])) {
swap(_con[parent], _con[child]);
}
if (child + 1 < _con.size() && _cmp(_con[child], _con[child + 1])) {
child++;
}
比较大小操作符写死不便用户修改,使用宏定义,函数指针都比较复杂,还有一种简单的方式就是仿函数。
仿函数又名函数对象,本质是对象,通过重载()
操作符模仿函数的调用方式。
仿函数相当于更高级的泛型,使用仿函数能够改变执行逻辑,仿函数内部的实现完全由用户自定,拥有极大的自定义空间。
template <class T>
struct less {
bool operator()(const T& left, const T& right) const {
return left < right;
}
};
template <class T>
struct greater {
bool operator()(const T& left, const T& right) const {
return left > right;
}
};
Less less;
less(1, 2);
Greater greater;
greater(1, 2);
仿函数本质是一种类型,所以可以作模版参数,让用户定义类的时候指定。
template <class T, class Container = vector<T>, class Compare = Less<T>>
class priority_queue {
void adjust_up(int child) {
if (_cmp(_con[parent], _con[child]))
//...
}
void adjust_down(int parent) {
if (child + 1 < _con.size() && _cmp(_con[child], _con[child + 1]))
//...
if (_cmp(_con[parent], _con[child]))
//...
}
Compare _cmp;
};
#include <iostream>
#include <vector>
namespace test
{
template<class T>
struct less
{
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
struct greater
{
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
template<class T, class Container = std::vector<T>, class Compare = less<T>>
class priority_queue
{
public:
void adjust_up(int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (_cmp(_con[parent], _con[child]))
swap(_con[parent], _con[child]);
else
break;
child = parent;
parent = (child - 1) / 2;
}
}
void adjust_down(int parent)
{
int child = parent * 2 + 1;
while (child < _con.size())
{
if (child + 1 < _con.size() && _cmp(_con[child], _con[child + 1]))
child++;
if (_cmp(_con[parent], _con[child]))
swap(_con[parent], _con[child]);
else
break;
parent = child;
child = parent * 2 + 1;
}
}
void push(const T& x)
{
_con.push_back(x);
adjust_up(_con.size() - 1);
}
void pop()
{
_con[0] = _con[_con.size() - 1];
_con.pop_back();
adjust_down(0);
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
T& top()
{
return _con[0];
}
private:
Container _con;
Compare _cmp;
};
}