priority_queue (优先队列)
优先队列是一种容器适配器,采用了堆这样的数据结构,保证了第一个元素总是整个优先队列中最大的(或最小的)元素。
优先队列默认使用vector作为底层存储数据的容器,在vector上使用了堆算法将vector中的元素构造成堆的结构,所以其实我们就可以把它当作堆,凡是需要用堆的位置,都可以考虑优先队列。(所以需要先学习堆)
优先队列性质
priority_queue默认使用vector作为底层容器。
默认情况下priority_queue是大堆。
需要注意的是,priority_queue
并不支持直接访问和修改除了优先级最高元素外的其他元素。如果需要对特定元素进行操作,通常需要先将其取出,然后再进行操作,最后再将其放回优先队列中。
头文件
#include <queue>
初始化
定义容器和比较函数:然后,你需要定义一个priority_queue
对象,并指定元素类型和可选的比较函数(默认为std::less
)。
std::priority_queue<int, std::vector<int>, std::less<int>> pq;
priority_queue的参数
priority_queue<class T, vector<T>, less<T>>
class T:T是优先队列中存储的元素的类型。
vector<T>,T是优先队列中存储的元素的类型。
less<T>,T是优先队列中存储的元素的类型。并且堆顶是最大值
// priority_queue类似于 qsort,构造函数的返回值为 1 时交换位置。若构造函数相同priority_queue的堆顶和qsort的首元素相同
常用内置函数
push() 插入元素到priority_queue
插入的元素会根据其优先级自动进行排序。
top() 返回优先级最高(堆顶)的元素
pop() 移除优先级最高的(堆顶)元素
empty() 检查priority_queue
是否为空。
下面的代码,演示了如何使用priority_queue
:
#include <queue>
#include <iostream>
int main() {
std::priority_queue<int, std::vector<int>, std::less<int>> pq;
pq.push(3);
pq.push(1);
pq.push(4);
pq.push(1);
while (!pq.empty()) {
std::cout << pq.top() << " ";
pq.pop();
}
//输出结果为:4 3 1 1
return 0;
}
priority_queue中的仿函数
在priority_queue
中,仿函数用于比较元素的优先级,并根据其返回值确定它们在队列中的位置。默认情况下,priority_queue
使用std::less
作为仿函数,也就是将元素按照从大到小的顺序进行排序。
你可以使用不同的仿函数来改变元素的排序方式。以下是一些常见的仿函数
std::less<T>
:对于基本数据类型和自定义类型,默认使用<
运算符进行比较,按照从大到小的顺序排序。std::greater<T>
:对于基本数据类型和自定义类型,默认使用>
运算符进行比较,按照从小到大的顺序排序。
除了上述默认提供的仿函数外,你还可以自定义仿函数来实现自定义的元素比较规则。
自定义仿函数需要满足严格弱排序的要求,即:
#include <iostream>
#include <queue>
#include <functional>
struct Person {
std::string name;
int age;
Person(const std::string& n, int a) : name(n), age(a) {}
};
// 自定义仿函数
struct CompareByAge {
bool operator()(const Person& p1, const Person& p2) const {
return p1.age > p2.age; // 按照年龄从小到大排序
}
};
int main() {
std::priority_queue<Person, std::vector<Person>, CompareByAge> pq;
pq.push(Person("Alice", 25));
pq.push(Person("Bob", 30));
pq.push(Person("Charlie", 20));
while (!pq.empty()) {
Person p = pq.top();
pq.pop();
std::cout << p.name << " - " << p.age << std::endl;
}
return 0;
}
输出结果为:
在上面的代码中,我们定义了一个名为CompareByAge的结构体,重载了函数调用运算符operator(),按照Person对象的age成员进行比较。然后,我们将CompareByAge作为优先队列的仿函数类型,并插入3个Person对象到优先队列中。最后,我们按照自定义的排序方式依次取出元素并输出。
比较类的函数参数
less类和greater类的函数参数。
上面是less类的内部函数,less类的内部重载(),这也就是前面提到的仿函数,参数列表中有左右两个参数,左边小于右边的时候返回true,此时优先队列就是大堆。
上面是greater类的内部函数,greater类的内部重载(),这也就是前面提到的仿函数,参数列表中有左右两个参数,左边大于右边的时候返回true,此时优先队列就是小堆。
注意:less类和greater类只能比较内置类型的数据的大小,如果用户需要比较自定义类型的数据,就需要自己定义一个比较类,并且重载()。
同时less类和greater类也具有模板参数,因为他们也是模板,所以我们如果要存储自定义类型的元素,就要将自定义类型作为模板参数传递给less类和greater类。
构造函数的参数列表
struct cmp
{
bool operator(const T& x, const T& y)
{
.....
}
};