cpp文章3-priority_queue和sort的自定义排序

1,010 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

本文是我在掘金平台发表于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…