priority_queue(优先队列)是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。
注意: 默认情况下priority_queue是大堆。
使用vector作为底层容器,内部构造大堆结构。
priority_queue<int, vector<int>, less<int>> q1;
使用vector作为底层容器,内部构造小堆结构。
priority_queue<int, vector<int>, greater<int>> q2;
不指定底层容器和内部需要构造的堆结构。
priority_queue<int> q;
注意: 此时默认使用vector作为底层容器,内部默认构造大堆结构。
成员函数 | 功能 |
---|---|
push | 插入元素到队尾(并排序) |
pop | 弹出队头元素(堆顶元素) |
top | 访问队头元素(堆顶元素) |
size | 获取队列中有效元素个数 |
empty | 判断队列是否为空 |
swap | 交换两个队列的内容 |
实列:
#include
#include
#include
using namespace std;
int main()
{
priority_queue<int> q;
q.push(3);
q.push(6);
q.push(0);
q.push(2);
q.push(9);
q.push(8);
q.push(1);
while (!q.empty())
{
cout << q.top() << " ";
q.pop();
}
cout << endl; //9 8 6 3 2 1 0
return 0;
}
仿函数也叫函数对象,是定义了一个含有operator()成员函数的对象,可以视为一个一般的函数,只不过这个函数功能是在一个类中的运算符operator()中实现,它将函数作为参数传递的方式来使用。也就是将类按照函数的使用方式使用。
仿函数在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
class MyAdd
{
public:
int operator()(int v1, int v2)
{
return v1 + v2;
}
};
//1.函数对象在使用时,可以像普通函数那样调用,可以有参数和返回值
void test01()
{
MyAdd myAdd;
cout << myAdd(1, 2) << endl;//3
}
仿函数的优点:
1.仿函数可有拥有自己的数据成员和成员变量,比一般函数灵活。
2.仿函数比函数指针的执行速度快。
3.仿函数可以作为模板参数使用,因为每个仿函数都拥有自己的类型。
缺点:
1.需要单独实现一个类。
2.定义形式比较复杂。
priority_queue的底层实际上就是堆结构,故其核心就是实现向上调整和向下调整算法。两种算法的具体细节参考堆的详解
#pragma once
#include
#include
#include
using namespace std;
namespace lhj_3
{
//仿函数/函数对象 ---类重载operator()
//也就是类对象可以像函数一样去使用
//比较方式(使内部结构为大堆)
template<class T>
class less
{
public:
bool operator()(const T& l,const T& r) const
{
return l < r;
}
};
//比较方式(使内部结构为小堆)
//仿函数
template<class T>
class greater
{
public:
bool operator()(const T& l, const T& r) const
{
return l > r;
}
};
//优先级队列的模拟实现
template<class T ,class Container=vector<T>, class compare=less<T>>
//compare是进行比较的仿函数 ---less 为大堆 greater为小堆
class priority_queue
{
public:
//构造函数
priority_queue()
{}
//迭代器区间构造
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
while (first!=last)
{
_con.push_back(*first);
++first;
}
//建堆
for (int i = (_con.size()-1-1)/2; i > 0; i--)
{
adjust_down(i);
}
}
//向上调整
void adjust_up(size_t child)
{
size_t parent = (child - 1) / 2;
while (chile>0)
{
//下面两个等价,compare为仿函数
//if (_con[parent]<_con[child] )
if (com(_con[parent] , _con[child]))
{
std::swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
//插入元素到队尾(并排序)
void push(const T& x)
{
_con.push_back(x);
adjust_up(_con.size() - 1);
}
//向下调整
void adjust_down(size_t parent)
{
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]))
{
std::swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//弹出队头元素(堆顶元素)
void pop()
{
std::swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
adjust_down(0);
}
private:
Container _con;//底层容器
Compare com; //比较方式
};
}
反向迭代器也就是迭代器适配器,可以适配生成任何支持++,–的容器的反向迭代器
反向迭代器大致框架
#pragma once
namespace it
{
//复用,迭代器适配器
template<class Iterator,class Ref,class Ptr>
struct __reverse_iterator
{
Iterator _cur;
typedef __reverse_iterator<Iterator, Ref> RIterator;
//用正向迭代器构造出一个反向迭代器
__reverse_iterator(Iterator it)
:_cur(it)
{}
RIterator operator++()
{
--_cur;
return *this;
}
RIterator operator--()
{
++_cur;
return *this;
}
Ref operator*()
{
auto tmp = _cur;
//rbegin指向正向迭代器中end的位置
//而end为尾结点的下一个节点的位置,即头结点
//故需要将tmp--后再解引用
--tmp;
return *tmp;
}
Ptr operator->()
{
return &(operator*());
}
bool operator!=(const RIterator& it)
{
return _cur != it._cur;
}
};
}