C++STL容器priority_queue(优先队列)简述


目录
  • 优先队列的默认实现
  • 使用优先队列来实现小根堆
  • 优先队列+结构体
  • 自定义优先队列的比较规则

优先队列的默认实现

STL容器中提供了 priority_queue(优先队列) 来实现类似堆的功能。

为了方便说明其用法,接下来的讲述中直接将 priority_queue 看做堆来讲述。

和用于排序的 sort 函数一样,priority_queue 默认的比较规则都是 <(小于号)。

sort 默认会根据小于号将元素从小到大排序;

priority_queue 中的元素默认是根据小于号的比较规则将最大的作为其堆顶元素。这个跟 sort 的思路有点不一样, priority_queue 是 “我比你小,则我把你推到顶上去” 的意思。

就是说,默认情况下,priority_queue 中的元素总是最大的那个作为堆顶元素。

所以默认的 priority_queue 是一个大根堆

定义一个 priority_queue 的一般格式为:

priority_queue<类型名> 容器名;

其最常用的成员方法有:

  • push(a):往堆中推入一个元素 a
  • top():获得堆顶元素;
  • pop():弹出堆顶元素;
  • empty():判断容器是否为空(为空返回true);
  • size():返回目前该容器中包含元素的个数。

示例程序:

#include 
using namespace std;
priority_queue que;
int main() {
    for (int i = 3; i <= 6; i ++)
        que.push(i);
    que.push(1);
    que.push(8);
    cout << "size = " << que.size() << endl;
    while (!que.empty()) {
        cout << que.top() << ",";
        que.pop();
    }
    return 0;
}

输出结果:

size = 6
8,6,5,4,3,1,

使用优先队列来实现小根堆

默认的比较规则是 <(小于号),但是我们也可以在定义 priority_queue 的时候讲规则进行修改。比如下面的代码段就定义了一个大根堆:

priority_queue, greater > que;

其中,priority_queue 后的尖括号中:

  • int 表示数据类型;
  • vector 表示数据的存储方式,在这里是使用 vector 存储(据说这样写上会快一些,不过我这里主要还是为了占一个位置,方便写第三个参数);
  • greater 表示比较规则,这个 greater 对应的比较规则就是 >(大于号),即 “我比你大,我把你推到顶上去”。

需要注意的是,如果 priority_queue 存储的是别的类型的数据,则对应的数据类型都得进行相应的修改,如下下面的代码段定义了一个存储 double 类型数据的小根堆:

priority_queue, greater > que;

示例程序(优先队列实现小根堆):

#include 
using namespace std;
priority_queue, greater > que;
int main() {
    for (int i = 3; i <= 6; i ++)
        que.push(i);
    que.push(1);
    que.push(8);
    cout << "size = " << que.size() << endl;
    while (!que.empty()) {
        cout << que.top() << ",";
        que.pop();
    }
    return 0;
}

输出结果:

size = 6
1,3,4,5,6,8,

优先队列+结构体

对于结构体类型的变量来说,默认没有 <(小于号),这种情况下直接使用该结构体类型的 priority_queue 显然是不行的(会报错)。

所以可以考虑为新定义的结构体类型定义一个 < 的功能,这种操作被称作 重载运算符

比如,下面的程序中,我将定义一个名为 Node 的结构体并为其重载 < 运算符(因为 priority_queue 默认看的就是 < 运算符),并实现一个 Node 类型的优先队列。示例程序:

#include 
using namespace std;
struct Node {
    int x, y;
    // 重载小于号运算符
    bool operator < (const Node b) const {
        return x < b.x || x == b.x && y < b.y;
    }
};
priority_queue que;   // 使用Node默认的比较规则

int main() {
    que.push({3, 5});
    que.push({2, 4});
    que.push({1, 3});
    que.push({4, 2});
    que.push({3, 3});
    while (!que.empty()) {
        Node u = que.top();
        que.pop();
        cout << "(" << u.x << " , " << u.y << ")" << endl;
    }
    return 0;
}

程序输出结果:

(4 , 2)
(3 , 5)
(3 , 3)
(2 , 4)
(1 , 3)

自定义优先队列的比较规则

有的时候,有的数据类型可能已经封装好了 < 运算符,或者其 < 运算符还有别的用处,这种情况下我们不能再为其重载 < 运算符,那么这个时候怎么办呢?

回顾一下,在使用 sort 函数的时候,我们定义过比较函数(一般取名为 cmp,国际惯例)。

然后在 sort 的时候讲 cmp 函数作为 sort 函数的第三个参数。

在 priority_queue 中也可以使用类型的功能,只不过 priority_queue 使用的是 比较结构体

我们可以定义一个名为 Cmp 的结构体并重载其 () 运算符,然后将其作为 priority_queue 定义时尖括号中的第三个参数。比如,下面的程序为 Node 类型匹配了一个对应的 Cmp 类型,并使用 Cmp 的比较规则实现了一个优先队列。

#include 
using namespace std;
struct Node {
    int x, y;
};
struct Cmp {    // 比较结构体
    bool operator () (Node &a, Node &b) {
        return a.x < b.x || a.x == b.x && a.y < b.y;
    }
};
priority_queue, Cmp> que;   // 定义que时使用Cmp作为比较规则
int main() {
    que.push({3, 5});
    que.push({2, 4});
    que.push({1, 3});
    que.push({4, 2});
    que.push({3, 3});
    while (!que.empty()) {
        Node u = que.top();
        que.pop();
        cout << "(" << u.x << " , " << u.y << ")" << endl;
    }
    return 0;
}

输出结果:

(4 , 2)
(3 , 5)
(3 , 3)
(2 , 4)
(1 , 3)