STL中的优先队列总结

     在我之前的C++:STL标准入门汇总中提到了:“优先队列(priority_queue)元素的次序是由作用于所存储的值对上的某种谓词决定的的有序队列”,另一篇《面试必问之堆排序及堆》的最后也提到了优先队列的内部实现是用堆来实现的,今天总结下,优先队列的用法及内部实现。

      优先队列,顾名思义,就是一种根据一定优先级存储和取出数据的队列。它可以说是队列和排序的完美结合体,不仅可以存储数据,还可以将这些数据按照我们设定的规则进行排序。

优先队列的实现

   有一点需要澄清,很多人一直以为Priority Queue就是一个Priority Heap,这种说法当然是片面的。既然优先队列只是存储数据和排序的结合,那么根据我们学过的知识,可以列出以下的实现方法:无序数组、无序链表、有序数组、有序链表以及二叉查找树,当然还有堆。这些方法在实现中当然也有优先级,下表就是一些方法的实现基本操作的时间性能对比:



图1.一些优先队列的实现方案的时间性能对比


       概括来说,无序的数据结构在插入时较简单直接插入即可,但删除和检索时要找出最小的或最大的,因而复杂度较高。有序的数据结构正好相反。从这张表里我们可以按照时间复杂度这个优先级来进行一次排序,当然,你会选用最后一种二叉查找树作为实现优先队列的方案,但是实际上我们采用的是堆。堆,就是一种二叉树,堆和二叉查找树是类似的,但是也有些许不同:从形状来看,二叉查找树可以有不同的形状,而堆只能是完全二叉树;在时间性能上,二者并无明显的区别。堆也是有序的,我们一般将其分为大顶堆和小顶堆。小顶堆,顾名思义,就是这个堆的子结点的值小于父结点的值;相反的,大顶堆就是堆的子结点的值都大于父结点的值。
     在实现的时候,我们常常使用基于数组的堆,即堆中的元素保存在一个数组中,而这个数组元素的保存也是按照一定规律的:如果父结点的位置为n,那么,其对应的左右子结点的位置分别是2n+1和2n+2。也就是说,我们在视觉上(如果用画图形象化表示堆的话)和本质上将堆打扮成两种东西,这样既便于理解,也利于实现,本质的实现是用数组是因为考虑到数组的一些性能。
(以上参照了 http://februus.iteye.com/blog/1288305)

        从另外一个角度看,有序数组或是有序链表保存的数据是有序的(二叉查找树的中序遍历也是有序的,也可以认为二叉查找树是有序的),但优先队列并不要求保存的数据是完全按顺序保存的,只是要求能最快速地找到最大或最小的那一个,这一点就决定了其他数据结构的不太适合,而堆结构正好符合这个性质。

    

优先队列的用法

 priority_queue 对于基本类型的使用方法相对简单。他的模板声明带有三个参数:

      priority_queue<Type, Container, Functional>     其中Type 为数据类型, Container 为保存数据的容器,Functional 为元素比较方式。
Container 必须是用数组实现的容器,比如 vector, deque 但不能用 list.STL里面默认用的是 vector. 比较方式默认用 operator< , 所以如果你把后面俩个参数缺省的话,优先队列就是大顶堆,队头元素最大。

#include <iostream>
#include <queue> 
using namespace std; 
int main(){
    priority_queue<int> q;     
    for( int i= 0; i< 10; ++i ) q.push( rand() );
    while( !q.empty() ){
        cout << q.top() << endl;
        q.pop();
    }     
    getchar();
    return 0;
}
如果要用到小顶堆,则一般要把模板的三个参数都带进去。
STL里面定义了一个仿函数 greater<>,对于基本类型可以用这个仿函数声明小顶堆

#include <iostream>
#include <queue> 
using namespace std; 
int main(){
    priority_queue<int, vector<int>, greater<int> > q;
    for( int i= 0; i< 10; ++i ) q.push( rand() );
    while( !q.empty() ){
        cout << q.top() << endl;
        q.pop();
    }     
    getchar();
    return 0;
}

对于自定义类型,则必须自己重载 operator< 或者自己写仿函数

#include <iostream>
#include <queue> 
using namespace std; 
struct Node{
    int x, y;
    Node( int a= 0, int b= 0 ):
        x(a), y(b) {}
};
 
bool operator<( Node a, Node b ){
    if( a.x== b.x ) return a.y> b.y;
    return a.x> b.x; 
}
 
int main(){
    priority_queue<Node> q;     
    for( int i= 0; i< 10; ++i )
    q.push( Node( rand(), rand() ) );     
    while( !q.empty() ){
        cout << q.top().x << ' ' << q.top().y << endl;
        q.pop();
    }     
    getchar();
    return 0;
}
自定义类型重载 operator< 后,声明对象时就可以只带一个模板参数。 重载函数operator< 也可以放到Node结构体申明里,但要加friend修饰
但此时不能像基本类型这样声明
priority_queue<Node, vector<Node>, greater<Node> >;
原因是 greater<Node> 没有定义,如果想用这种方法定义则可以按如下方式:
#include <iostream>
#include <queue> 
using namespace std; 
struct Node{
    int x, y;
    Node( int a= 0, int b= 0 ):
        x(a), y(b) {}
};
 
struct cmp{
    bool operator() ( Node a, Node b ){
        if( a.x== b.x ) return a.y> b.y;         
        return a.x> b.x; }
};
 
int main(){
    priority_queue<Node, vector<Node>, cmp> q;     
    for( int i= 0; i< 10; ++i )
    q.push( Node( rand(), rand() ) );
     
    while( !q.empty() ){
        cout << q.top().x << ' ' << q.top().y << endl;
        q.pop();
    }     
    getchar();
    return 0;
}  
//以上代码实现的是一个小顶堆

自实现优先队列

#include <iostream>
#include <algorithm>
#include <vector>
 
using namespace std;
 
class priority_queue
{
    private:
        vector<int> data;         
    public:
        void push( int t ){ 
            data.push_back(t); 
            push_heap( data.begin(), data.end()); 
        }         
        void pop(){
            pop_heap( data.begin(), data.end() );
            data.pop_back();
        }         
        int top() { return data.front(); }
        int size() { return data.size(); }
        bool empty() { return data.empty(); }
};  
int main()
{
    priority_queue test;
    test.push( 3 );
    test.push( 5 );
    test.push( 2 );
    test.push( 4 );     
    while( !test.empty() ){
        cout << test.top() << endl;
        test.pop(); 
    }         
    return 0; 
}

(参照 http://www.cnblogs.com/flyoung2008/articles/2136485.html)


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页