目录
一、概念:
优先级队列(priority_queue)在功能上类似于堆(heap)这个数据结构,在优先级队列中,元素的出队顺序(或检索顺序)基于它们的优先级,而不是它们进入队列的顺序。具有最高优先级的元素将最先出队,而具有最低优先级的元素将最后出队(或反之,取决于具体实现),就像是在大堆中堆顶元素是堆中最大的值,在删除堆顶元素时总是删除的最大值,而不是取决于它们入堆的顺序。
定义:
#include <queue> // 引头文件
// 类似于大堆; less表示按照递减(从大到小)的顺序插入元素
priority_queue<int, vector<int>, less<int>> s;
// 类似于小堆; greater表示按照递增(从小到大)的顺序插入元素
priority_queue<int, vector<int>, greater<int>> s;
二、仿函数(Functor):
概念:
仿函数(Functor)也称为函数对象,是一种可调用的对象,一般通过重载函数调用操作符(operator())来实现。这使得仿函数类具有像函数一样的特性:可以接受参数并返回值。
仿函数的目的为:使一个类的使用看上去像一个函数,从而方便地应用于算法、STL容器以及其他需要函数对象的场合。
仿函数的优点:
1、可以拥有状态,这意味着仿函数可以在运行时动态地改变其行为。
2、与纯函数相比,仿函数在某些情况下可能具有更快的执行速度。
注意:
仿函数并不是真正的函数,而是具有函数行为的类对象。因此,它们可以像类一样存储和访问数据,同时又具有函数的调用特性。
应用:
例如在C++ algorithm 库 中的 sort 函数 的第三个参数:
template<class RandomIt, class Compare>
void sort(RandomIt first, RandomIt last, Compare comp);
comp:(可选):一个接受两个参数并返回一个布尔值的函数或函数对象。
默认的排序是升序,此时我们不用库中提供的降序仿函数,自己实现一个简单的降序仿函数:
template<class T>
struct Greater // 函数对象(仿函数)功能实现
{
bool operator()(const T& x, const T& y){
return x > y;
}
};
void Test()
{
vector<int> v = { 5,3,7,8,3,0,6,3,1,2,9 };
sort(v.begin(), v.end(), Greater<int>()); // 匿名调用
// Greater<int> s1;
// sort(v.begin(), v.end(), s1); // 显式调用
for (auto e : v){
cout << e << " ";
}
}
三、底层实现:
基本操作:
1、push():将元素入队列。【操作与堆类似,向尾部插入并不断向上调整至合适位置】
2、pop():删除队列中优先级最高的元素。【操作与堆类似,首尾交换后删除尾部,队头元素不断向下调整至合适位置】
3、top():获取并返回队列中优先级最高的元素。
4、size():获取并返回队列大小。【返回值为 unsigned int(size_t)类型】
5、empty():判断队列是否为空。【返回值为bool类型。队列为空返回true,不空返回false】
完整代码:
template<class T>
struct Less
{
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
struct Greater
{
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
template<class T, class Con = vector<T>, class Com = Less<T>>
class priority_queue
{
public:
// 向上调整
void adjust_up(int child)
{
Com com; // 模板实例化一个函数对象,默认Less,取小
int parent = (child - 1) / 2;
while (child > 0)
{
//if (_con[child] > _con[parent])
// if(_con[parent] < _con[child])
if (com(_con[parent], _con[child])) // 为真执行,为假跳出
{
swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
// 向下调整
void adjust_down(size_t parent)
{
Com com;
size_t child = parent * 2 + 1;
while (child < _con.size())
{
// 找左右孩子中大的那个与父节点交换,换算关系如下
//if (child + 1 < _con.size() && _con[child + 1] > _con[child])
//if (child + 1 < _con.size() && _con[child] < _con[child + 1])
if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
{
++child;
}
//if (_con[child] > _con[parent])
//if (_con[parent] < _con[child])
if (com(_con[parent], _con[child]))
{
swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void push(const T& val)
{
_con.push_back(val);
adjust_up(_con.size() - 1); // 传入刚插入的子节点下标
}
void pop()
{
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
adjust_down(0);
}
const T& top() const
{
return _con[0];
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
private:
Con _con;
};
测试示例:
给一个数组(升序)以此按序进入队列,此时优先级队列的功能类似大堆,应该按照降序返回:
int main()
{
vector<int> v = { 1,2,3,4,5,6,7,8,9,10 };
priority_queue<int> q1; // 默认大堆 Less
for (auto i : v)
{
q1.push(i);
}
while (!q1.empty())
{
cout << q1.top() << " ";
q1.pop();
}
return 0;
}
测试结果: