优先队列是一个特殊的队列,普通队列是先进先出(FIFO)的,而优先队列是根据元素的大小(优先级)决定元素的出队顺序。
#include
#include
int main()
{
std::priority_queue<int> pq;
pq.push(1);
pq.push(10);
pq.push(20);
pq.push(18);
pq.push(3);
pq.push(12);
int size = pq.size();
for (int i=0; i < size; i++)
{
std::cout<<pq.top()<<' ';
pq.pop();
}
std::cout<<std::endl;
return 0;
}
# 运行结果:
20 18 12 10 3 1
priority_queue默认是从大到小排序,是因为priority_queue使用了一个默认的比较行为,下面介绍如何自定义比较行为从而按照我们的意愿得到想要的结果。
先让我们看一下优先队列在STL中的说明。
/**
* @brief A standard container automatically sorting its contents.
*
* @ingroup sequences
*
* @tparam _Tp Type of element.
* @tparam _Sequence Type of underlying sequence, defaults to vector<_Tp>.
* @tparam _Compare Comparison function object type, defaults to
* less<_Sequence::value_type>.
*/
template<typename _Tp, typename _Sequence = vector<_Tp>,
typename _Compare = less<typename _Sequence::value_type> >
class priority_queue
{}
可以看到priority_queue类模板实际有三个参数.
前两个模板参数很好理解,第三个参数比较特殊,_Compare
是一个函数对象(也叫仿函数)。函数对象是一个行为类似于函数的对象,也是通过类来定义的。函数对象可以使得函数功能更加的泛化,STL的算法中很多都使用了函数对象,允许用户可以通过函数对象自定义函数的行为。比如:sort函数接收一个函数对象允许自定义比较行为。
priority_queue默认的函数对象是less,less的定义如下:
/// One of the @link comparison_functors comparison functors@endlink.
template<typename _Tp>
struct less : public binary_function<_Tp, _Tp, bool>
{
_GLIBCXX14_CONSTEXPR
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x < __y; }
};
less
就是一个函数对象类,其继承了binary_function
。类内自定义了一个函数operate()
,所有的函数对象都是通过operate()
发挥作用的。在以函数对象为参数的函数内部通过调用函数对象的operate()
方法实现函数行为的泛化。
void algorithm(arg1, arg2,..,,functorObj)
{
...
functorObj();
...
}
通过functorObj() 便可以调用函数对象的operate()
方法,这种语法类似于Python
的callable对象(定义了 __call__ 方法的对象)。我们通过重写operate()
或者编写新的operate()
方法就可以定制算法的行为。
STL标准库定义了多个用于比较的函数对象,如less
、less_equal
、greater
、greater_equal
等
std::priority_queue<int, std::vector<int>, greater<int>> pq; //从小到大的优先队列
std::priority_queue<int, std::vector<int>, less<int>> pq; //从大到小的优先队列
因为priority_queue定义的最后两个参数都是默认参数,如果需要传入第三个参数,那么第二参数也需要传入。
我们可以看到less等函数对象都是通过模板实现的,且在operate()
方法使用的是比较运算符。因此可以通过重载类的比较运算符定制priority_queue的行为。
struct Test
{
int num;
char c;
bool operator > (const Test& obj) const
{
return this->num > obj.num;
}
bool operator < (const Test& obj) const
{
return this->num < obj.num;
}
}Test;
int main()
{
priority_queue<Test, vector<Test>, less<Test>> pq1;
priority_queue<Test, vector<Test>, greater<Test>> pq2;
return 0;
}
因为priority_queue第三个参数需要的是一个函数对象,我们可以定义自己定义一个函数对象定制priority_queue的行为。
//函数对象QueueCmp
struct QueueCmp
{
bool operator()(const vector<int> &v1, const vector<int> &v2)const
{
return v1[0] > v2[0];
}
};
int main()
{
priority_queue<vector<int>, vector<vector<int>>, QueueCmp> pq;
return 0;
}
优先队列实际上是一个堆, 在STL中,优先队列默认是一个大顶堆, 从排序角度看, 就是降序排序(从大到小),因此可以通过堆的相关算法实现优先队列。
#include
#include
#include
template<class T>
class PriorityQueue
{
private:
std::vector<T> data;
public:
PriorityQueue()
{
}
bool empty() const
{
return data.size()? false:true;
}
int size() const
{
return data.size();
}
T top() const
{
if (!this->empty())
{
return this->data[0];
}
return 0;
}
void push(T data)
{
this->pushHeap(data);
}
void pop()
{
this->popHeap();
}
private:
void popHeap()
{
this->data.erase(this->data.begin()); //delete the top
this->adjustHeap(0, this->data.size()); //adjust heap
}
void adjustHeap(int i, int length)
{
for(int child = 2*i+1; child < length; child = 2*i+1)
{
if(child + 1 < length && this->data[child+1] > this->data[child])
{
child += 1;
}
if(this->data[child] > this->data[i])
{
this->swap(this->data[child], this->data[i]);
i = child;
}
else
{
break; // adjust over
}
}
}
void pushHeap(T data)
{
this->data.push_back(data);
int end = this->data.size()-1;
for(int i=floor(end*0.5 - 0.5); i>=0; i=floor(i*0.5 - 0.5))
{
this->adjustHeap(i, end+1);
}
}
void swap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
};
int main()
{
PriorityQueue<int> pq;
pq.push(1);
pq.push(10);
pq.push(20);
pq.push(18);
pq.push(3);
pq.push(12);
int size = pq.size();
for (int i=0; i < size; i++)
{
std::cout<<pq.top()<<' ';
pq.pop();
}
std::cout<<std::endl;
return 0;
}