[STL]priority_queue多种方式自定义排序

一、背景

在做leetcode题目时很多题都需要使用优先队列(堆),并需要使用自定义数据类型、自定义有限队列的排序方式。本文对priority_queue的自定义排序方式做了总结。本文可能并不能覆盖所有自定义方式,若读者有建议或本文存在纰漏,请在本文下留言,不胜感激。

二、priority_queue概述

Priority queues are a type of container adaptors, specifically designed such that its first element is always the greatest of the elements it contains, according to some strict weak ordering criterion.

优先队列是一类容器适配器,该容器满足:按照某种严格弱序排列,该容器的第一个元素总是所有元素中最大(最小)的。

priority_queue 的模板参数:

template <class T, class Container = vector<T>,
  class Compare = less<typename Container::value_type> > class priority_queue;

T:元素类型。
Container:低层存储容器类型,默认为vector< T >类型。
Compare:两个参数且返回值为bool类型的双参判断式,默认为less< T >判断式。

三、priority_queue自定数据类型和自定义排序方式

假设使用priority_queue存储自定义类型Node,Node数据结构如下:

struct Node{
	int size;
	int price;
}

不同的自定义排序方式如下:

1. 使用自定义类型比较关系

即重载数据类型Node的 < 、>运算符,使得Node类型的对象可以使用<,>符号进行比较。代码如下:

struct Node {
    int size;
    int price;
    // 重载<运算符
    bool operator<(const Node &b) const {
        return this->size == b.size ? this->price > b.price : this->size < b.size;
    }
};

int main() {
    priority_queue<Node> priorityQueue;
    for (int i = 0; i < 5; i++) {
        priorityQueue.push(Node{i, 5 - i});
    }
    while (!priorityQueue.empty()) {
        Node top = priorityQueue.top();
        cout << "size:" << top.size << " price:" << top.price << endl;
        priorityQueue.pop();
    }
    return 0;
}

输出结果为:

size:4 price:1
size:3 price:2
size:2 price:3
size:1 price:4
size:0 price:5

由于priority_queue中默认使用less判断式比较元素大小,因此可以使用对Node类型数据进行 < 符号的重载以满足可以使用less的要求()。
假如需要使用

    priority_queue<Node, vector<Node>, greater<Node>> priorityQueue;

初始化优先队列,还需要对Node的>符号进行重载,代码如下:

struct Node {
    int size;
    int price;
    // 重载>运算符
    bool operator>(const Node &b) const {
        return this->size == b.size ? this->price < b.price : this->size > b.size;
    }
};

另外,也可以使用其他重载方式对Node数据类型的<、>符号进行重载,这是c++中运算符重载的内容,这里就不再展开了。
需要注意的是:
在Node结构体内进行运算符重载使,重载函数一定要声明const类型,不然编译不通过。

2. 使用仿函数(函数对象)

使用仿函数(函数对象)作为Compare双参判断式。代码如下:

struct Node {
    int size;
    int price;
};
class Cmp {
public:
    bool operator()(const Node &a, const Node &b) {
        return a.size == b.size ? a.price > b.price : a.size < b.size;
    }
};
int main() {
    priority_queue<Node, vector<Node>, Cmp> priorityQueue;
    for (int i = 0; i < 5; i++) {
        priorityQueue.push(Node{i, 5 - i});
    }
    while (!priorityQueue.empty()) {
        Node top = priorityQueue.top();
        cout << "size:" << top.size << " price:" << top.price << endl;
        priorityQueue.pop();
    }
    return 0;
}

输出结果为:

size:4 price:1
size:3 price:2
size:2 price:3
size:1 price:4
size:0 price:5

需要注意的是:使用仿函数对优先队列进行自定义排序,需要在声明priority_queue对象时显式地定义Container类型和Compare类型,即:

priority_queue<Node, vector<Node>, Cmp> priorityQueue;

3. 使用lambda表达式

c++中lamdbda表达式相关的知识也很多,这里不讨论lambda表达式中详细的细节问题,如有需要可以参考C++ lambda表达式。
使用lambda表达式对priority_queue自定义排序的代码如下:

auto cmp = [](const Node &a, const Node &b) { return a.size == b.size ? a.price > b.price : a.size < b.size; };
priority_queue<Node, vector<Node>, decltype(cmp)> priorityQueue(cmp);

输出结果依旧不变。
另外,由于priority_queue中的Compare模板已经确定,是一个两个参数输入,返回bool值的判断式,因此使用:

priority_queue<Node, vector<Node>, function<bool(const Node&, const Node&)>> priorityQueue(cmp);

已经可以正常运行。
需要注意的是:

  • 使用lambda表达式时需要在priorityQueue对象构造函数中传入lambda表达式对象,即priorityQueue(cmp);
  • 使用function时需要包含头文件,并且函数的输入参数必须和lambda表达式的输入参数类型相同。

4. 使用函数指针

使用函数指针与3. 使用lambda表达式类似,都是在priority_queue<.,.,Cmp>中定义Compare的类型同时在priorityQueue(cmp)的中输入具体的对象作为参数,不过 这里使用的是函数和函数的指针(地址)而不是lambda表达式对象。
具体代码如下:

bool cmpFun(const Node &a, const Node &b) {
    return a.size == b.size ? a.price > b.price : a.size < b.size;
}
bool (*p)(const Node &, const Node &) = cmpFun;
priority_queue<Node, vector<Node>, decltype(*p)> priorityQueue(*p);

或者:

priority_queue<Node, vector<Node>, decltype(*cmpFun)> priorityQueue(*cmpFun);
priority_queue<Node, vector<Node>, decltype(cmpFun)*> priorityQueue(cmpFun);

类似的,decltype()也可以使用function代替。

四、参考引用

[1]. std::priority_queue
[2]. c++优先级队列priority_queue compare成员参数分析
[3]. C++ lambda表达式
[4]. C++ std::优先级队列priority_queue

五、修正记录

  1. [20210908] 修改了使用lambda表达式function<>的错误代码。function作为一个模板,两端应该为<>,而不是()。正确代码如下:
priority_queue<Node, vector<Node>, function<bool(const Node&, const Node&)>> priorityQueue(cmp);

你可能感兴趣的:(C++,STL,stl,priority_queue)