优先队列容器与队列一样,只能从队尾插入元素,从队首删除元素。但是它有一个特性,就是队列中最大的元素总是位于队首,所以出队时,并非按照先进先出的原则进行,而是将当前队列中最大的元素出队。这点类似于给队列里的元素进行了由大到小的顺序排序。元素的比较规则默认按元素值由大到小排序,可以重载“<”操作符来重新定义比较规则。
优先级队列可以用向量(vector)或双向队列(deque)来实现(注意list container不能用来实现queue,因为list的迭代器不是任意存取iterator,而pop中用到堆排序时是要求randomaccess iterator 的!):
priority_queue<vector<int>, less<int> > pq1; // 使用递增less函数对象排序
priority_queue<deque<int>, greater<int> > pq2; // 使用递减greater函数对象排序
和队列基本操作相同:
top 访问队头元素
empty 队列是否为空
size 返回队列内元素个数
push 插入元素到队尾 (并排序)
emplace 原地构造一个元素并插入队列
pop 弹出队头元素
swap 交换内容
在默认的优先队列中,优先级高的先出队。在默认的int型中先出队的为较大的数。
头文件:
#include
声明方式:
priority_queue<int> q; //通过操作,按照元素从大到小的顺序出队
priority_queue<int,vector<int>, greater<int> > q;//通过操作,按照元素从小到大的顺序出队
struct cmp {
operator bool ()(int x, int y)
{
return x > y; // x小的优先级高
//也可以写成其他方式,如: return p[x] > p[y];表示p[i]小的优先级高
}
};
priority_queue<int, vector<int>, cmp> q; //定义方法
struct node {
int x, y;
friend bool operator < (node a, node b)
{
return a.x > b.x; //结构体中,x小的优先级高
}
};
priority_queue<node>q; //定义方法
在该结构中,y为值, x为优先级。
通过自定义operator<操作符来比较元素中的优先级。
在重载”<”时,最好不要重载”>”,可能会发生编译错误
#include
#include
#include
#include
using namespace std;
struct cmp1
{
bool operator()(int &a,int &b)
{
return a > b;//最小值优先
}
};
struct cmp2
{
bool operator()(int &a, int &b)
{
return a < b;//最大值优先
}
};
struct MyStruct
{
string name;
int age;
bool operator < (const MyStruct &o) const
{
return age > o.age;
}
};
struct MyStruct2
{
string name;
int age;
bool operator < (const MyStruct2 &o) const
{
return age < o.age;
}
};
void main()
{
//1.默认是最大的排在最前面。
priority_queue<int> m_queue;
// priority_queue,less >m_queue;//和上面是一样的效果
m_queue.push(1);
m_queue.push(3);
m_queue.push(5);
m_queue.push(2);
m_queue.push(4);
m_queue.push(6);
cout << "最大的在最前面:\n";
while (!m_queue.empty())
{
cout << m_queue.top() << " ";
m_queue.pop();
}
cout << endl;
cout << "最小的在最前面:\n";
//2.如何最小的在前面呢?(greater在functional模板里)
//注意“>>”会被认为错误,
//这是右移运算符,所以这里用空格号隔开
priority_queue<int, vector<int>, greater<int> > m_queue2;
m_queue2.push(1);
m_queue2.push(3);
m_queue2.push(5);
m_queue2.push(2);
m_queue2.push(4);
m_queue2.push(6);
while (!m_queue2.empty())
{
cout << m_queue2.top() << " ";
m_queue2.pop();
}
cout << endl;
//3.自定义 最小值
cout << "自定义最小值优先:\n";
priority_queue<int, vector<int>, cmp1> m_queue3;
m_queue3.push(7);
m_queue3.push(9);
m_queue3.push(11);
m_queue3.push(8);
m_queue3.push(10);
m_queue3.push(12);
while (!m_queue3.empty())
{
cout << m_queue3.top() << " ";
m_queue3.pop();
}
cout << endl;
//4.自定义 最大值
cout << "自定义最大值优先:\n";
priority_queue<int, vector<int>, cmp2> m_queue4;
m_queue4.push(7);
m_queue4.push(9);
m_queue4.push(11);
m_queue4.push(8);
m_queue4.push(10);
m_queue4.push(12);
while (!m_queue4.empty())
{
cout << m_queue4.top() << " ";
m_queue4.pop();
}
cout << endl;
//5.自定义结构体1
cout << "自定义结构体1:\n";
priority_queue<MyStruct> m_queue5;
MyStruct my1;
my1.name = "jack1";
my1.age = 22;
m_queue5.push(my1);
MyStruct my2;
my2.name = "jack2";
my2.age = 28;
m_queue5.push(my2);
MyStruct my3;
my3.name = "jack3";
my3.age = 25;
m_queue5.push(my3);
MyStruct my4;
my4.name = "jack1";
my4.age = 20;
m_queue5.push(my4);
while (!m_queue5.empty())
{
MyStruct temp = m_queue5.top();
cout << temp.name.c_str() << " " << temp.age << endl;
m_queue5.pop();
}
cout << endl;
//6.自定义结构体
cout << "自定义结构体2:\n";
priority_queue<MyStruct2> m_queue6;
MyStruct2 my12;
my12.name = "jack1";
my12.age = 22;
m_queue6.push(my12);
MyStruct2 my22;
my22.name = "jack2";
my22.age = 28;
m_queue6.push(my22);
MyStruct2 my32;
my32.name = "jack3";
my32.age = 25;
m_queue6.push(my32);
MyStruct2 my42;
my42.name = "jack1";
my42.age = 20;
m_queue6.push(my42);
while (!m_queue6.empty())
{
MyStruct2 temp = m_queue6.top();
cout << temp.name.c_str() << " " << temp.age << endl;
m_queue6.pop();
}
cout << endl;
system("pause");
}
至于优先队列的原理其实就是大顶堆、小顶堆的结构。具体实现可以看看我的另一篇文章:算法设计之-世界上最好理解的堆排序
先来看个图对它有个印象
接下来看看源码:
template <class T, class Sequence = vector<T>,
class Compare = less<typename Sequence::value_type> >
class priority_queue {
public:
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_reference;
protected:
Sequence c; // 底層容器
Compare comp; // 元素大小比較標準
public:
priority_queue() : c() {}
explicit priority_queue(const Compare& x) : c(), comp(x) {}
// 以㆘用到的 make_heap(), push_heap(), pop_heap()都是泛型演算法
// 注意,任㆒個建構式都立刻於底層容器內產生㆒個 implicit representation heap。
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last, const Compare& x)
: c(first, last), comp(x) { make_heap(c.begin(), c.end(), comp); }
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
: c(first, last) { make_heap(c.begin(), c.end(), comp); }
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
const_reference top() const { return c.front(); }
void push(const value_type& x) {
__STL_TRY {
// push_heap 是泛型演算法,先利用底層容器的 push_back() 將新元素
// 推入末端,再重排 heap。見 C++ Primer p.1195。
c.push_back(x);
push_heap(c.begin(), c.end(), comp); // push_heap 是泛型演算法
}
__STL_UNWIND(c.clear());
}
void pop() {
__STL_TRY {
// pop_heap 是泛型演算法,從 heap 內取出㆒個元素。它並不是真正將元素
// 彈出,而是重排 heap,然後再以底層容器的 pop_back() 取得被彈出
// 的元素。見 C++ Primer p.1195。
pop_heap(c.begin(), c.end(), comp);
c.pop_back();
}
__STL_UNWIND(c.clear());
}
};
从源码class Sequence = vector
可以看出 底层容器默认使用的是vector
priority_queue的所有元素,进出都有一定的规则,只有queue顶端的元素才有机会被外界取用,priority_queue不提供走访功能,也不提供迭代器。
其实主要也就是插入一个元素后,然后调整堆结构,使其满足大顶堆,或者小顶堆的性质。更重要的关注大小顶堆加入元素后的一个调整。
所以好好看看我刚提到的那篇文章。