優先隊列的詳解與使用

1.優先隊列

  1. 堆的元素個數爲N,高度不超過logN+1
  2. 節點N的父節點在N/2初,左節點在N*2右節點在N*2+1處。

常用代碼:

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
 
struct cmp1{
booloperator ()(int &a,int &b)
{
returna>b;
}
};
 
int main()
{
constint len =5;
inti;
inta[len]={3,5,9,6,2};
priority_queue<int,vector<int>,cmp1>qi;
 
for(i=0;i<len;i++)
qi.push(a[i]);//放入元素
for(i=0;i<len;i++)
{
cout<<qi.top()<<"";
qi.pop();
}
cout<<endl;
return0;
}


概括所有知識點的代碼:

優先隊列:顧名思義,首先它是一個隊列,但是它強調了“優先”二字,所以,已經不能算是一般意義上的隊列了,它的“優先”意指取隊首元素時,有一定的選擇性,即根據元素的屬性選擇某一項值最優的出隊~
百度百科上這樣描述的:
  優先級隊列 是不同於先進先出隊列的另一種隊列。每次從隊列中取出的是具有最高優先權的元素
  優先隊列的類定義  
  優先隊列是0個或多個元素的集合,每個元素都有一個優先權或值,對優先隊列執行的操作有1) 查找;2) 插入一個新元素;3) 刪除.在最小優先隊列(min priorityq u e u e)中,查找操作用來搜索優先權最小的元素,刪除操作用來刪除該元素;對於最大優先隊列(max priority queue),查找操作用來搜索優先權最大的元素,刪除操作用來刪除該元素.優先權隊列中的元素可以有相同的優先權,查找與刪除操作可根據任意優先權進行.
優先隊列,其構造及具體實現我們可以先不用深究,我們現在只需要瞭解其特性,及在做題中的用法,相信,看過之後你會收穫不少。
使用優先隊列,首先要包函STL頭文件"queue",
以一個例子來解釋吧(呃,寫完才發現,這個代碼包函了幾乎所有我們要用到的用法,仔細看看吧):
view plaincopy to clipboardprint?
/*優先隊列的基本使用    2010/7/24    dooder*/ 

#include<stdio.h>  
#include<functional>  
#include<queue>  
#include<vector>  
using namespace std;  
//定義結構,使用運算符重載,自定義優先級1  
struct cmp1{  
    bool operator ()(int &a,int &b){  
        return a>b;//最小值優先  
    }  
};  
struct cmp2{  
    bool operator ()(int &a,int &b){  
        return a<b;//最大值優先  
    }  
};  
//定義結構,使用運算符重載,自定義優先級2  
struct number1{  
    int x;  
    bool operator < (const number1 &a) const {  
        return x>a.x;//最小值優先  
    }  
};  
struct number2{  
    int x;  
    bool operator < (const number2 &a) const {  
        return x<a.x;//最大值優先  
    }  
};  
int a[]={14,10,56,7,83,22,36,91,3,47,72,0};  
number1 num1[]={14,10,56,7,83,22,36,91,3,47,72,0};  
number2 num2[]={14,10,56,7,83,22,36,91,3,47,72,0};  
  
int main()  
{   priority_queue<int>que;//採用默認優先級構造隊列  
  
    priority_queue<int,vector<int>,cmp1>que1;//最小值優先  
    priority_queue<int,vector<int>,cmp2>que2;//最大值優先  
  
    priority_queue<int,vector<int>,greater<int> >que3;//注意“>>”會被認爲錯誤,  
                                                      //這是右移運算符,所以這裏用空格號隔開  
    priority_queue<int,vector<int>,less<int> >que4;////最大值優先  
  
    priority_queue<number1>que5;  
    priority_queue<number2>que6;  
  
    int i;  
    for(i=0;a[i];i++){  
        que.push(a[i]);  
        que1.push(a[i]);  
        que2.push(a[i]);  
        que3.push(a[i]);  
        que4.push(a[i]);  
    }  
    for(i=0;num1[i].x;i++)  
        que5.push(num1[i]);  
    for(i=0;num2[i].x;i++)  
        que6.push(num2[i]);  
  
  
    printf("採用默認優先關係:\n(priority_queue<int>que;)\n");  
    printf("Queue 0:\n");  
    while(!que.empty()){  
        printf("%3d",que.top());  
        que.pop();  
    }  
    puts("");  
    puts("");  
  
    printf("採用結構體自定義優先級方式一:\n(priority_queue<int,vector<int>,cmp>que;)\n");  
    printf("Queue 1:\n");  
    while(!que1.empty()){  
        printf("%3d",que1.top());  
        que1.pop();  
    }  
    puts("");  
    printf("Queue 2:\n");  
    while(!que2.empty()){  
        printf("%3d",que2.top());  
        que2.pop();  
    }  
    puts("");  
    puts("");  
    printf("採用頭文件\"functional\"內定義優先級:\n(priority_queue<int,vector<int>,greater<int>/less<int> >que;)\n"); 
    printf("Queue 3:\n");  
    while(!que3.empty()){  
        printf("%3d",que3.top());  
        que3.pop();  
    }  
    puts("");  
    printf("Queue 4:\n");  
    while(!que4.empty()){  
        printf("%3d",que4.top());  
        que4.pop();  
    }  
    puts("");  
    puts("");  
    printf("採用結構體自定義優先級方式二:\n(priority_queue<number>que)\n");  
    printf("Queue 5:\n");  
    while(!que5.empty()){  
        printf("%3d",que5.top());  
        que5.pop();  
    }  
    puts("");  
    printf("Queue 6:\n");  
    while(!que6.empty()){  
        printf("%3d",que6.top());  
        que6.pop();  
    }  
    puts("");  
    return 0;  
}  


/*
運行結果 :
採用默認優先關係:
(priority_queue<int>que;)
Queue 0:
83 72 56 47 36 22 14 10  7  3
 
採用結構體自定義優先級方式一:
(priority_queue<int,vector<int>,cmp>que;)
Queue 1:
 7 10 14 22 36 47 56 72 83 91
Queue 2:
83 72 56 47 36 22 14 10  7  3
 
採用頭文件"functional"內定義優先級:
(priority_queue<int,vector<int>,greater<int>/less<int> >que;)
Queue 3:
 7 10 14 22 36 47 56 72 83 91
Queue 4:
83 72 56 47 36 22 14 10  7  3
 
採用結構體自定義優先級方式二:
(priority_queue<number>que)
Queue 5:
 7 10 14 22 36 47 56 72 83 91
Queue 6:
83 72 56 47 36 22 14 10  7  3
*/ 
運行結果:
採用默認優先關係:
(priority_queue<int>que;)
Queue 0:
83 72 56 47 36 22 14 10  7  3
採用結構體自定義優先級方式一:
(priority_queue<int,vector<int>,cmp>que;)
Queue 1:
 7 10 14 22 36 47 56 72 83 91
Queue 2:
83 72 56 47 36 22 14 10  7  3
採用頭文件"functional"內定義優先級:
(priority_queue<int,vector<int>,greater<int>/less<int> >que;)
Queue 3:
 7 10 14 22 36 47 56 72 83 91
Queue 4:
83 72 56 47 36 22 14 10  7  3
採用結構體自定義優先級方式二:
(priority_queue<number>que)
Queue 5:
 7 10 14 22 36 47 56 72 83 91
Queue 6:
83 72 56 47 36 22 14 10  7  3
好了,如果你仔細看完了上面的代碼,那麼你就可以基本使用優先隊列了,下面給出一些我做題中有過的一些應用,希望能給大家帶來一些啓
示~
1、先來一個我們最近做的題吧,http://acm.hdu.edu.cn/showproblem.php?pid=1242
題意:某人被關在囚籠裏等待朋友解救,問能否解救成功,最少需要多少時間~
具體:可同時有幾個朋友,每走一格消耗一分鐘的時間 ,地圖上還存在着衛兵,衛兵可以解決掉,但是要另外花費一分鐘~
分析:從“a”出發,此題可以用回溯法進行深搜,但那樣做的話,效率還是不能讓人滿意,但是廣搜的話,由於入隊後每次出隊時,根據地
圖情況的不同,出隊元素所記憶的時間並不是層次遞增的,因此使用簡單廣搜的話,同樣需要全部搜索才能找到正確答案。有沒有一種方法能
讓某一步因爲遇到士兵而多花時間的結點在隊列中向後推遲一層出隊呢?答案是肯定的,在這裏我們可以用優先隊列來實現,總體思想上是,
根據時間進行優先性選擇,每次都要出隊當前隊列元素中記錄時間最少的出隊,而入隊處理時,我們可以按順序對四個方向上的各種情況按正
常處理入隊就行了,出隊順序由優先隊列根據預設優先性自動控制。這樣,我們就可以從“a”進行基於優先隊列的範圍搜索了,並且在第一
次抵達有朋友的位置時得到正確結果~具體實現代碼:
view plaincopy to clipboardprint?
/*HDU 1242  基於優先隊列的範圍搜索,16ms   dooder*/ 
 
#include<stdio.h>  
#include<queue>  
using namespace std;  
  
#define M 201  
typedef struct p{  
    int x,y,t;  
    bool operator < (const p &a)const  
    {  
        return t>a.t;//取時間最少優先  
    }  
}Point;  
  
char map[M][M];  
Point start;  
int n,m;  
int dir[][2]={{1,0},{-1,0},{0,1},{0,-1}};  
  
int bfs()  
{  
    priority_queue<Point>que;  
    Point cur,next;  
    int i;  
  
    map[start.x][start.y]='#';  
    que.push(start);  
    while(!que.empty()){  
        cur=que.top();//由優先隊列自動完成出隊時間最少的元素  
        que.pop();  
        for(i=0;i<4;i++){  
            next.x=cur.x+dir[i][0];  
            next.y=cur.y+dir[i][1];  
            next.t=cur.t+1;  
            if(next.x<0||next.x>=n||next.y<0||next.y>=m)  
                continue;  
            if(map[next.x][next.y]=='#')  
                continue;  
            if(map[next.x][next.y]=='r')  
                return next.t;  
            if(map[next.x][next.y]=='.'){  
                map[next.x][next.y]='#';  
                que.push(next);  
            }  
            else if(map[next.x][next.y]=='x'){  
                map[next.x][next.y]='#';  
                next.t++;  
                que.push(next);  
            }  
        }  
    }  
    return -1;  
}  
int main()  
{  
    int i,ans;  
    char *p;  
    while(scanf("%d%d",&n,&m)!=-1){  
        for(i=0;i<n;i++){  
            scanf("%s",map[i]);  
            if(p=strchr(map[i],'a')){  
                start.x=i;  
                start.y=p-map[i];  
                start.t=0;  
            }  
        }  
        ans=bfs();  
        printf(ans+1?"%d\n":"Poor ANGEL has to stay in the prison all his life.\n",ans); 
    }  
    return 0;  
}  


2、http://acm.hdu.edu.cn/showproblem.php?pid=1053
題意:給出一行字符串,求出其原編碼需要的編碼長度和哈夫曼編碼所需的長度,並求其比值
分析:根據哈夫曼生成樹的生成過程可知,其生成樹的權值是固定的而且這個值是最小的,而且其值根據生成樹的順序,我們可以找出規律而
不需要真的去生成一棵樹然後再求出權值,其模擬過程爲取出隊列中權值最小的兩個元素,將其值加入結果中,然後將這兩個元素的權值求和
即得出其父節點的權值,將生成元素作爲結點入隊~~如此循環,直至取出隊列中最後兩個元素加入結果,實現代碼如下:
view plaincopy to clipboardprint?
/*HDU 1053  採用廣搜求哈夫曼生成樹的權值 0ms   dooder*/ 
#include<stdio.h>  
#include<string.h>  
#include<ctype.h>  
#include<functional>  
#include<queue>  
using namespace std;  
#define M 1000050  
char str[M];  
int list[27];  
  
priority_queue< int,vector<int>,greater<int> >que;  
  
int main()  
{  
    int ans,sum;  
    int i,a,b,c;  
    while(scanf("%s",str),strcmp(str,"END")){  
        memset(list,0,sizeof(list));  
        for(i=0;str[i];i++){  
            if(isalpha(str[i]))  
                list[str[i]-'A']++;  
            else  
                list[26]++;  
        }  
        sum=i*8;ans=i;c=0;  
        for(i=0;i<27;i++){  
            if(list[i]){  
                que.push(list[i]);  
                c++;  
            }  
        }         
        if(c>1){ans=0;//注意只有一種字符的情況  
            while(que.size()!=1){  
                a=que.top();  
                que.pop();  
                b=que.top();  
                que.pop();  
                ans+=a+b;  
                que.push(a+b);  
            }  
            while(!que.empty())//使用後清空隊列  
                que.pop();  
        }  
        printf("%d %d %.1f\n",sum,ans,1.0*sum/ans);  
    }  
    return 0;  
} 

 
3、http://acm.pku.edu.cn/JudgeOnline/problem?id=2263
這是第二次練習賽時,我們做過的最後一題,這裏採用優先隊列進行實現,在《誰說不能這樣做題》中已提到這種方法,在這裏再次放出代
碼,~
題意:給出各城市間道路的限制載重量,求出從一個城市到另外一個城市的貸車能夠運載的最大貨物重量。
分析:採用優先隊列,每次取出當前隊列中結點的minheavy最大值出隊,對它的連接結點搜索入隊,這樣,從出發點開始就可以
在到達終點時求出結果,即最大載貨物重,實現代碼如下:
view plaincopy to clipboardprint?
/*POJ 2263  16ms  dooder*/ 
 
#include<stdio.h>  
#include<string.h>  
#include<queue>  
using namespace std;  
#define M 201  
typedef struct w{  
    int city;  
    int mintons;  
    bool operator < (const w &a)const {  
        return mintons < a.mintons;  
    }//優先性定義  
}Way;  
char citys[M][31];  
int map[M][M];  
bool mark[M][M];  
int n,m,from,to,ans,k;  
priority_queue <Way> que;  
int min(int a,int b)  
{  
    return a>b?b:a;  
}  
void bfs()  
{  
    Way cur,next;  
    int i;  
    while(!que.empty()){  
        cur=que.top();  
        que.pop();  
        if(cur.city==to){  
            if(cur.mintons>ans)  
                ans=cur.mintons;  
            while(!que.empty())  
                que.pop();  
            return ;  
        }  
        for(i=0;i<n;i++){  
            if(map[cur.city][i]&&!mark[cur.city][i]){  
                next.city=i;  
                next.mintons=min(cur.mintons,map[cur.city][i]);  
  
                mark[cur.city][i]=mark[i][cur.city]=1;  
                que.push(next);  
            }  
        }  
    }  
}  
void run()  
{  
    int i,temp,index;  
    Way cur;  
    ans=0;  
    memset(mark,0,sizeof(mark));  
    temp=0;  
    for(i=0;i<n;i++){  
        if(map[from][i]>temp){  
            temp=map[from][i];  
            index=i;  
        }  
    }  
    cur.city=index;  
    cur.mintons=temp;  
    que.push(cur);  
    bfs();  
}  
int main()  
{  
    int k1,k2,tons,t=1;  
    char s1[31],s2[31];  
    while(scanf("%d%d",&n,&m),n||m){  
        k=0;  
        while(m--){  
            scanf("%s%s%d",s1,s2,&tons);  
            for(k1=0;strcmp(s1,citys[k1])&&k1<k;k1++);  
            if(k1==k)  
                strcpy(citys[k++],s1);  
            for(k2=0;strcmp(s2,citys[k2])&&k2<k;k2++);  
            if(k2==k)  
                strcpy(citys[k++],s2);  
            map[k1][k2]=map[k2][k1]=tons;  
        }  
        scanf("%s%s",s1,s2);  
        for(from=0;strcmp(citys[from],s1);from++);  
        for(to=0;strcmp(citys[to],s2);to++);  
        run();  
        printf("Scenario #%d\n",t++);  
        printf("%d tons\n\n",ans);  
    }  
    return 0;  
} 

 
當然了,優先隊列的用法決不是僅僅提到的這些,各種應用還需要大家去發現,給道題大家可以練習一下hdu 2066\
相信大家已經學到不少了,還有一點可以告訴大家,優先隊列是啓發式搜索的數據結構基礎,希望好好理解,並逐步掌握其用法~
 加:失策啊,竟然忘了說優先隊列的效率了,其時間複雜度爲O(logn).n爲隊列中元素的個數,存取都需要消耗時間~


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章