|
|
|
|
## 关于优先队列$priority\_queue$大小根堆、重载操作符的说明
|
|
|
|
|
|
|
|
|
|
[感谢原作者](https://www.cnblogs.com/chenleideblog/p/12745271.html)
|
|
|
|
|
|
|
|
|
|
### 一、关于$priority\_queue$的说明
|
|
|
|
|
|
|
|
|
|
#### 内部实现
|
|
|
|
|
|
|
|
|
|
`priority_queue`默认情况下,以$vector$为底层容器,加上$heap$(默认$max-heap$) 处理规则;**形成大根堆**。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$priority\_queue$被归为 $container$ $adapter$,也就是对 $container$ 进行封装一层。
|
|
|
|
|
|
|
|
|
|
`priority_queue`操作规则上是 $queue$,只允许在尾部加入元素,并从首部取出元素;只不过内部元素具有优先级,优先级高者先出。
|
|
|
|
|
|
|
|
|
|
`priority_queue`的所有元素进出具有一定规则,所以 **不提供遍历功能**,也不提供迭代器。
|
|
|
|
|
|
|
|
|
|
#### 疑惑产生
|
|
|
|
|
下面为`priority_queue`的使用规则,第一个传入了类型,第二个为容器类型,第三个为**比较函数**。
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
template<
|
|
|
|
|
class T,
|
|
|
|
|
class Container = std::vector<T>,
|
|
|
|
|
class Compare = std::less<typename Container::value_type> //comp默认为less
|
|
|
|
|
> class priority_queue;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
疑惑关键就在于比较函数。
|
|
|
|
|
|
|
|
|
|
`priority_queue`默认形成大根堆,而传入的$comp$默认为$less$。
|
|
|
|
|
|
|
|
|
|
#### 为何传入一个可将序列顺序调为有小到大的函数,建成的堆反而是大顶堆呢?
|
|
|
|
|
|
|
|
|
|
不知你们有没有这种感觉?直觉上认为传入$less$,建成小顶堆,而传入$greater$,建成大顶堆。
|
|
|
|
|
|
|
|
|
|
#### 源码解析
|
|
|
|
|
|
|
|
|
|
`std::less()`源码:若`__x < __y`,则返回$true$,顺序不变,否则,顺序发生变化。这个函数名含义与实现效果相一致。
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
struct less : public binary_function<_Tp, _Tp, bool>
|
|
|
|
|
{
|
|
|
|
|
_GLIBCXX14_CONSTEXPR
|
|
|
|
|
bool
|
|
|
|
|
operator()(const _Tp& __x, const _Tp& __y) const
|
|
|
|
|
{ return __x < __y; }
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`make_heap`中调用的`__push_heap`源码:
|
|
|
|
|
```c++
|
|
|
|
|
__push_heap(_RandomAccessIterator __first, _Distance __holeIndex,
|
|
|
|
|
_Distance __topIndex, _Tp __value, _Compare __comp)
|
|
|
|
|
//__holeIndex 新添加节点的索引,即叫做孔洞
|
|
|
|
|
//__topIndex 顶端索引
|
|
|
|
|
//__value 新添加节点的值
|
|
|
|
|
//__comp 比较函数,传入为less
|
|
|
|
|
{
|
|
|
|
|
_Distance __parent = (__holeIndex - 1) / 2; //找到新节点父节点索引
|
|
|
|
|
while (__holeIndex > __topIndex && __comp(*(__first + __parent), __value)) {
|
|
|
|
|
//若孔洞没有到达最顶端 && 父节点的值小于新添加节点的值,则要进行下列操作
|
|
|
|
|
//less中,左边比右边小则返回true,与less愿意相同
|
|
|
|
|
*(__first + __holeIndex) = *(__first + __parent); //将父节点的值放入孔洞
|
|
|
|
|
__holeIndex = __parent; //孔洞的索引编程了原来父节点的索引,往上移动了
|
|
|
|
|
__parent = (__holeIndex - 1) / 2; //那么孔洞的父节点又要继续往上找
|
|
|
|
|
}
|
|
|
|
|
*(__first + __holeIndex) = __value; //将新添加节点的值放在找到的位置(孔洞)
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
经过上面源码分析,传入的$comp$就是为了在比较 **孔洞节点** 与 **父节点** 的大小,若返回$true$, **才会进行交换操作** 。
|
|
|
|
|
|
|
|
|
|
所以传入$less$,返回$true$是因为 **父节点比孔洞节点小**,所以要进行交换,则将大的值移动到前面,所以建成的堆为 **大顶堆**。
|
|
|
|
|
|
|
|
|
|
而传入$greater$,返回$true$是因为 **父节点** 比 **孔洞节点大**,所以进行交换,则将大的值移动到了后面,所以建成的堆为 **小顶堆**。
|
|
|
|
|
|
|
|
|
|
所以,就明白了为什么传入$less$反而形成了大根堆,而传入$greater$则形成了小根堆。
|
|
|
|
|
|
|
|
|
|
### 二、如何使用
|
|
|
|
|
```c++
|
|
|
|
|
#include <queue>
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
priority_queue<int> que; //默认定义了最大堆,等同于将第三个参数使用less<int>
|
|
|
|
|
priority_queue<int, vector<int>, less<int>> que; //定义大根堆
|
|
|
|
|
|
|
|
|
|
priority_queue<int, vector<int>, greater<int>> que; //定义小根堆,VS下需要加入头文件#include<functional>
|
|
|
|
|
|
|
|
|
|
//测试 priority_queue<int> que;
|
|
|
|
|
que.push(3);
|
|
|
|
|
que.push(5);
|
|
|
|
|
que.push(4);
|
|
|
|
|
cout << que.top() << endl; //5
|
|
|
|
|
|
|
|
|
|
//测试 priority_queue<int, vector<int>, less<int>> que;
|
|
|
|
|
que.push(3);
|
|
|
|
|
que.push(5);
|
|
|
|
|
que.push(4);
|
|
|
|
|
cout << que.top() << endl; //5
|
|
|
|
|
|
|
|
|
|
//测试 priority_queue<int, vector<int>, greater<int>> que;
|
|
|
|
|
que.push(3);
|
|
|
|
|
que.push(5);
|
|
|
|
|
que.push(4);
|
|
|
|
|
cout << que.top() << endl; //3
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 三、关于自定义优先级
|
|
|
|
|
上面给出了在处理整型等基本数据类型时直接出入$less$或者$greater$既可以建立相应的大顶堆或者小顶堆。若是处理结构体呢?如何定义优先级呢?
|
|
|
|
|
|
|
|
|
|
#### 普通数据类型
|
|
|
|
|
基本数据类型的比较函数可以直接使用`less<int>`或者`greater<int>`可以满足建立大根堆或者小根堆。
|
|
|
|
|
|
|
|
|
|
#### 结构体
|
|
|
|
|
对于结构体而言,将结构体放入优先队列中,比较函数需要建立在针对结构体的具体成员。
|
|
|
|
|
|
|
|
|
|
**自定义优先级的四种方法:**
|
|
|
|
|
|
|
|
|
|
* <以成员函数重载
|
|
|
|
|
```c++
|
|
|
|
|
struct Node { //我们将Node节点放入优先队列中希望以value进行比较
|
|
|
|
|
Node(int _id, int _value) : id(_id), value(_value){}
|
|
|
|
|
int id;
|
|
|
|
|
int value;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//大根堆
|
|
|
|
|
bool operator < (const Node& a, const Node& b){
|
|
|
|
|
return a.value < b.value; //将value的值由大到小排列,形成Node的大根堆
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
struct Node node1(1, 5);
|
|
|
|
|
struct Node node2(2, 3);
|
|
|
|
|
struct Node node3(3, 4);
|
|
|
|
|
|
|
|
|
|
priority_queue<Node> que;
|
|
|
|
|
|
|
|
|
|
que.push(node1);
|
|
|
|
|
que.push(node2);
|
|
|
|
|
que.push(node3);
|
|
|
|
|
|
|
|
|
|
cout << que.top().value << endl; //5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//小根堆
|
|
|
|
|
bool operator < (const Node& a, const Node& b)
|
|
|
|
|
{
|
|
|
|
|
return a.value > b.value; //将value的值由小到大排列,形成Node的小根堆
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cout << que.top().value << endl; //3
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
我试了 `bool operator > ()`,结果会报二进制“<”: 没有找到接受`const Node`类型的左操作数的运算符(或没有可接受的转换) 错误,我想原因应该是这样吧:`priority_queue`中默认的比较函数为`less`,`less`函数中只用到了 `{ return __x < __y; }`,所以重载中若只重载了`>`,函数找不到`<`,所以会出现错误。
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
struct less : public binary_function<_Tp, _Tp, bool>{
|
|
|
|
|
_GLIBCXX14_CONSTEXPR
|
|
|
|
|
bool
|
|
|
|
|
operator()(const _Tp& __x, const _Tp& __y) const
|
|
|
|
|
{ return __x < __y; }
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
* 自定义比较函数
|
|
|
|
|
```c++
|
|
|
|
|
struct cmp{
|
|
|
|
|
bool operator ()(const Node& a, const Node& b){
|
|
|
|
|
return a.value < b.value;//将value的值由大到小排列,形成Node的大根堆
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
priority_queue<Node, vector<Node>, cmp>q;
|
|
|
|
|
cout << que.top().value << endl; //5
|
|
|
|
|
|
|
|
|
|
struct cmp{
|
|
|
|
|
bool operator ()(const Node& a, const Node& b){
|
|
|
|
|
return a.value > b.value;//将value的值由小到大排列,形成Node的小根堆
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
priority_queue<Node, vector<Node>, cmp>q;
|
|
|
|
|
cout << que.top().value << endl; //3
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
上述在传入用户自定义的比较函数,那么在建堆过程中使用的`comp`函数即为我们自定义的`cmp`,这样分析同上。
|
|
|
|
|
|
|
|
|
|
* < 以类成员函数重载
|
|
|
|
|
```c++
|
|
|
|
|
struct Node { //我们将Node节点放入优先队列中希望以value进行比较
|
|
|
|
|
Node(int _id, int _value) : id(_id), value(_value){}
|
|
|
|
|
int id;
|
|
|
|
|
int value;
|
|
|
|
|
//大根堆
|
|
|
|
|
bool operator < (const Node& b) const //注意,此处若没有const则会报错
|
|
|
|
|
{
|
|
|
|
|
return value < b.value; //将value的值由大到小排列,形成Node的大根堆
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
cout << que.top().value << endl; //5
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
* <以友元函数重载 【这个和竞赛关系不大,暂时不用考虑】
|
|
|
|
|
```c++
|
|
|
|
|
struct Node{
|
|
|
|
|
int id;
|
|
|
|
|
int value;
|
|
|
|
|
friend bool operator<(const Node& a,const Node& b){
|
|
|
|
|
return a.value<b.value; //按value从大到小排列
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
priority_queue<Node>q;
|
|
|
|
|
```
|