优先队列理解与实现

优先队列:顾名思义,首先它是一个队列,但是它强调了优先二字,所以,已经不能算是一般意义上的队列了,它的优先意指取队首元素时,有一定的选择性,即根据元素的属性选择某一项值最优的出队~

百度百科上这样描述的:

  优先级队列 是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素

  优先队列的类定义  

  优先队列是0个或多个元素的集合,每个元素都有一个优先权或值,对优先队列执行的操作有1) 查找;2) 插入一个新元素;3) 删除.在最小优先队列(min priorityq u e u e),查找操作用来搜索优先权最小的元素,删除操作用来删除该元素;对于最大优先队列(max priority queue),查找操作用来搜索优先权最大的元素,删除操作用来删除该元素.优先权队列中的元素可以有相同的优先权,查找与删除操作可根据任意优先权进行.

优先队列,其构造及具体实现我们可以先

不用深究,我们现在只需要了解其特性,及在做题中的用法,相信,看过之后你会收获不少。

以一个最常见的例子来说明下:

struct cmpl {
	int x, y;
};

bool operator < (cmpl a, cmpl b)
{
	if (a.x == b.x)
		return a.y > b.y;//第二优先级,队顶为最小值
	else
		return a.x < b.x;//第一优先级,队顶为最大值
}

然后使用的时候就可以定义一个该结构体类型的优先队列即可:

struct cmpl cmp;
priority_queueQ;
cmp.x = 3;
cmp.y = 5;
Q.push(cmp);

当然优先队列有多种多样的用法,上面那种是最常见也是我用的最多的一种吧,基本上已经适用了大部分的需求。如果想对优先队列有更深入的了解,下面介绍优先队列的其他几种用法:

#include "stdafx.h"
#include
#include  
#include
#include
using namespace std;
//定义结构,使用运算符重载,自定义优先级1  
struct cmp1 {
	bool operator ()(int &a, int &b) {
		return a>b;//最小值优先  
	}
};
struct cmp2 {
	bool operator ()(int &a, int &b) {
		return aa.x;//最小值优先  
	}
};
struct number2 {
	int x;
	bool operator < (const number2 &a) const {
		return xque;//采用默认优先级构造队列  

	priority_queue, cmp1>que1;//最小值优先  
	priority_queue, cmp2>que2;//最大值优先  

	priority_queue, greater >que3;//注意“>>”会被认为错误,  
														//这是右移运算符,所以这里用空格号隔开  
	priority_queue, less >que4;最大值优先  

	priority_queueque5;
	priority_queueque6;

	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_queueque;)\n");
	printf("Queue 0:\n");
	while (!que.empty()) {
		printf("%3d", que.top());
		que.pop();
	}
	puts("");
	puts("");

	printf("采用结构体自定义优先级方式一:\n(priority_queue,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,greater/less >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_queueque)\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("");
	system("pause");
	return 0;
}
/*
运行结果 :
采用默认优先关系:
(priority_queueque;)
Queue 0:
91 83 72 56 47 36 22 14 10  7  3

采用结构体自定义优先级方式一:
(priority_queue,cmp>que;)
Queue 1:
3  7 10 14 22 36 47 56 72 83 91
Queue 2:
91 83 72 56 47 36 22 14 10  7  3

采用头文件"functional"内定义优先级:
(priority_queue,greater/less >que;)
Queue 3:
3  7 10 14 22 36 47 56 72 83 91
Queue 4:
91 83 72 56 47 36 22 14 10  7  3

采用结构体自定义优先级方式二:
(priority_queueque)
Queue 5:
3  7 10 14 22 36 47 56 72 83 91
Queue 6:
91 83 72 56 47 36 22 14 10  7  3
*/
/*
运行结果:
采用默认优先关系 :
(priority_queueque;)
Queue 0 :
	91 83 72 56 47 36 22 14 10  7  3
	采用结构体自定义优先级方式一 :
	(priority_queue, cmp>que;)
	Queue 1 :
	3  7 10 14 22 36 47 56 72 83 91
	Queue 2 :
	91 83 72 56 47 36 22 14 10  7  3
	采用头文件"functional"内定义优先级 :
	(priority_queue, greater / less >que;)
	Queue 3 :
	3  7 10 14 22 36 47 56 72 83 91
	Queue 4 :
	91 83 72 56 47 36 22 14 10  7  3
	采用结构体自定义优先级方式二 :
	(priority_queueque)
	Queue 5 :
	3  7 10 14 22 36 47 56 72 83 91
	Queue 6 :
	91 83 72 56 47 36 22 14 10  7  3
*/

理解完上面的代码,并且亲自实现一遍之后,基本上优先队列也理解的差不多了,接下来用几个例题巩固一下。

1、HDU 1242

题意:某人被关在囚笼里等待朋友解救,问能否解救成功,最少需要多少时间~

具体:可同时有几个朋友,每走一格消耗一分钟的时间 ,地图上还存在着卫兵,卫兵可以解决掉,但是要另外花费一分钟~

分析:从“a”出发,此题可以用回溯法进行深搜,但那样做的话,效率还是不能让人满意,但是广搜的话,由于入队后每次出队时,根据地

图情况的不同,出队元素所记忆的时间并不是层次递增的,因此使用简单广搜的话,同样需要全部搜索才能找到正确答案。有没有一种方法能

让某一步因为遇到士兵而多花时间的结点在队列中向后推迟一层出队呢?答案是肯定的,在这里我们可以用优先队列来实现,总体思想上是,

根据时间进行优先性选择,每次都要出队当前队列元素中记录时间最少的出队,而入队处理时,我们可以按顺序对四个方向上的各种情况按正

常处理入队就行了,出队顺序由优先队列根据预设优先性自动控制。这样,我们就可以从“a”进行基于优先队列的范围搜索了,并且在第一

次抵达有朋友的位置时得到正确结果~具体实现代码:

view plaincopy to clipboardprint?
/*HDU 1242  基于优先队列的范围搜索,16ms   dooder*/ 

#include  
#include  
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_queueque;  
    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

2、HDU 1053

题意:给出一行字符串,求出其原编码需要的编码长度和哈夫曼编码所需的长度,并求其比值

分析:根据哈夫曼生成树的生成过程可知,其生成树的权值是固定的而且这个值是最小的,而且其值根据生成树的顺序,我们可以找出规律而

不需要真的去生成一棵树然后再求出权值,其模拟过程为取出队列中权值最小的两个元素,将其值加入结果中,然后将这两个元素的权值求和

即得出其父节点的权值,将生成元素作为结点入队~~如此循环,直至取出队列中最后两个元素加入结果,实现代码如下:

view plaincopy to clipboardprint?
/*HDU 1053  采用广搜求哈夫曼生成树的权值 0ms   dooder*/  
#include  
#include  
#include  
#include  
#include  
using namespace std;  
#define M 1000050  
char str[M];  
int list[27];  
  
priority_queue< int,vector,greater >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、POJ 2263

这是第二次练习赛时,我们做过的最后一题,这里采用优先队列进行实现,在《谁说不能这样做题》中已提到这种方法,在这里再次放出代

码,~

题意:给出各城市间道路的限制载重量,求出从一个城市到另外一个城市的贷车能够运载的最大货物重量。

分析:采用优先队列,每次取出当前队列中结点的minheavy最大值出队,对它的连接结点搜索入队,这样,从出发点开始就可以

在到达终点时求出结果,即最大载货物重,实现代码如下:

view plaincopy to clipboardprint?
/*POJ 2263  16ms  dooder*/  
 
#include  
#include  
#include  
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  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;itemp){  
            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

最后,优先队列作为启发式搜索的数据结构基础,用法决不仅仅是这些,还有很多其他的应用,希望大家能好好理解,并逐步掌握其用法。还有一道hdu2066,推荐大家可以做一做。

哦对了,优先队列的时间复杂度为O(logn),其中n为队列中元素的个数,因为存和取都需要消耗时间的哦~-~

你可能感兴趣的:(算法,数据结构篇,priority_queue)