本文已参与「新人创作礼」活动,一起开启掘金创作之路。
本文是我在掘金平台发表于2022年4月份的,从发表最初累计第13篇博客,希望大家关注我,我将会持续在后端和大数据等领域进行书写更多的文章。
摘要
本文的标题是《2204-13-c++-priority_queue和sort的自定义排序》,介绍了priority_queue和sort方法的自定义排序方式,比如如何重写operator<方法,以及自己写函数对象结构体,并且介绍了函数对象的概念。
priority_queue自定义排序
priority_queue, 又称优先队列,是C++的STL库的重要组成部分之一。
priority_queue的定义在头文件<queue>中,因此需要#include <queue>。
常用操作
- 压入:
push() - 弹出:
pop() - 取队首元素:
top()//与一般的队列不一样,不用front() - 判空:
empty() - 取大小:
size()
自定义类型
使用自定义类型,就要重载<运算符了。 例:
//程序例1
#include <queue>
#include <iostream>
using namespace std;
struct node
{
int value;
// 这个结尾的函数修饰符const必须有,因为这是一个重写的标准写法,否则编译不通过
bool operator < (const node &x) const {
return this -> value < x.value;
}
node(int _v): value(_v) {}
};
int main()
{
//默认是大顶堆,因此使用std::less<node>,需要重写 operator<
//反之,使用小顶堆,使用std::greater<node> 需要重写 operator>,否则编译失败
priority_queue<node> q;
q.push(node(3));
q.push(node(5));
q.push(node(10));
cout << q.size() << endl;
while(!q.empty())
{
node tmp = q.top();
q.pop();
cout << tmp.value << " ";
}
cout << endl;
return 0;
}
输出:
3
10 5 3
也可以使用友元函数的方式来写operator<
#include<queue>
#include<iostream>
using namespace std;
struct node
{
int x, y;
node(int x,int y):x(x),y(y){}
};
// 友元函数的方式
bool operator< (node a,node b)
{
if(a.x == b.x) return a.y >= b.y;
else return a.x > b.x;
}
int main()
{
priority_queue<node> pq; //只传node,但是node结构体的<运算符已被改变
for(int i = 1; i <= 5; i++)
for(int j = 1; j <= 5; j++)
pq.push(node(i,j));
while(!pq.empty())
{
cout<<pq.top().x<<" "<<pq.top().y<<endl;
pq.pop();
}
return 0;
}
很多时候,我们需要由小到大排序,这时候就需要这样:
priority_queue<int, vector<int>, greater<int> >pq;
//三个int要一致!
为什么是greater而不是 less呢?记住!想象堆排序的原理,greater的第一个参数是当前元素,第二个参数是子节点,如果当前元素比子节点大,就下沉,所以是小顶堆。less则相反。
函数对象
using int_pair = pair<int, int>;
struct cmp{
bool operator()(int_pair &a, int_pair &b){ // 函数对象
return a.first > b.first;
}
};
单反有 operator()(T a, T b)的结构体或者类都可以用作函数, 因此这种结构体被称为函数对象结构体,它的实例就是函数对象了。
std::less 和 std::greater 之类的就是函数对象模板。
如下,就是一个函数对象使用的例子:
//程序例2
#include <iostream>
#include <vector>
#include <memory>
#include <queue>
using namespace std;
using int_pair = pair<int, int>;
struct cmp{
bool operator()(int_pair &a, int_pair &b){
return a.first > b.first;
}
};
int main(){
priority_queue<int_pair , vector<int_pair>, cmp> que;// 第三个类参数,指定函数对象结构体
vector<int_pair> arr{{10,1},{2,4},{3,4},{1,9},{9,3}};
for(auto &item: arr) que.push(item);
while(!que.empty()){
auto &top = que.top();
cout << top.first << "," << top.second << '\t';
que.pop();
}
}
再反思一下,为什么可以直接使用 std::less 和 std::greater 作为排序方式呢?
因为 例如下面的写法
priority_queue<T, vector<T>, greater<T> > queue;
greater本身就调用了T的 operator>函数。
对比sort和priority_queue的排序
//程序例2
#include <iostream>
#include <vector>
#include <memory>
#include <queue>
using namespace std;
using int_pair = pair<int, int>;
struct cmp{
bool operator()(int_pair &a, int_pair &b){
return a.first > b.first;
}
};
int main(){
priority_queue<int_pair , vector<int_pair>, cmp> que;
vector<int_pair> arr{{10,1},{2,4},{3,4},{1,9},{9,3}};
for(auto &item: arr) que.push(item);
while(!que.empty()){
auto &top = que.top();
cout << top.first << "," << top.second << '\t';
que.pop();
}
cout << endl;
cout << string(100, '*') << endl;
auto arr2 = arr;
sort(arr2.begin(), arr2.end(), cmp());
for(auto &item: arr2){
cout << item.first << "," << item.second << '\t';
}
cout << endl;
}
输出为
1,9 2,4 3,4 9,3 10,1
**************************************************
10,1 9,3 3,4 2,4 1,9
我们可以看出来,sort算法和优先级队列的排序方式刚好相反,同一个自定义排序函数,sort是从大到小,而priority_queue是从下到大。
相关工作
CharCai48562在他的博客[1]里介绍了priority_queue的基本信息, priority_queue的基本API,构造函数,以及如何指定自定义类的排序方法,其方式是重写 operator<或者operator>。
而AAMahone在他的博客里[2]则扩充介绍了如何编写一个函数对象模板,并在priority_queue中使用来自定义排序方式。
libfeihu在他的文章里[3]详细剖析了快速排序的原理,看上去不错,但是我没有详细阅读。
参考
[1]CharCai48562,C++ STL之priority_queue优先队列,juejin.cn/post/684490…
[2]AAMahone, C++ priority_queue的自定义比较方式, blog.csdn.net/AAMahone/ar…
[3]libfeihu,std::sort的源码解析,feihu.me/blog/2014/s…
[4]W Jiang, 排序算法C++实现, wbjiang.com/%E6%8E%92%E…