优先队列详解(转载)
1 优先队列:顾名思义,首先它是一个队列,但是它强调了“优先”二字,所以,已经不能算是一般意义上的队列了,它的“优先”意指取队首元素时,有一定的选择性,即根据元素的属性选择某一项值最优的出队~ 2 百度百科上这样描述的: 3 优先级队列 是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素 4 优先队列的类定义 5 优先队列是0个或多个元素的集合,每个元素都有一个优先权或值,对优先队列执行的操作有1) 查找;2) 插入一个新元素;3) 删除.在最小优先队列(min priorityq u e u e)中,查找操作用来搜索优先权最小的元素,删除操作用来删除该元素;对于最大优先队列(max priority queue),查找操作用来搜索优先权最大的元素,删除操作用来删除该元素.优先权队列中的元素可以有相同的优先权,查找与删除操作可根据任意优先权进行. 6 优先队列,其构造及具体实现我们可以先不用深究,我们现在只需要了解其特性,及在做题中的用法,相信,看过之后你会收获不少。 7 使用优先队列,首先要包函STL头文件"queue", 8 以一个例子来解释吧(呃,写完才发现,这个代码包函了几乎所有我们要用到的用法,仔细看看吧): 9 view plaincopy to clipboardprint? 10 /*优先队列的基本使用 2010/7/24 dooder*/ 11 #include<stdio.h> 12 #include<functional> 13 #include<queue> 14 #include<vector> 15 using namespace std; 16 //定义结构,使用运算符重载,自定义优先级1 17 struct cmp1{ 18 bool operator ()(int &a,int &b){ 19 return a>b;//最小值优先 20 } 21 }; 22 struct cmp2{ 23 bool operator ()(int &a,int &b){ 24 return a<b;//最大值优先 25 } 26 }; 27 //定义结构,使用运算符重载,自定义优先级2 28 struct number1{ 29 int x; 30 bool operator < (const number1 &a) const { 31 return x>a.x;//最小值优先 32 } 33 }; 34 struct number2{ 35 int x; 36 bool operator < (const number2 &a) const { 37 return x<a.x;//最大值优先 38 } 39 }; 40 int a[]={14,10,56,7,83,22,36,91,3,47,72,0}; 41 number1 num1[]={14,10,56,7,83,22,36,91,3,47,72,0}; 42 number2 num2[]={14,10,56,7,83,22,36,91,3,47,72,0}; 43 44 int main() 45 { priority_queue<int>que;//采用默认优先级构造队列 46 47 priority_queue<int,vector<int>,cmp1>que1;//最小值优先 48 priority_queue<int,vector<int>,cmp2>que2;//最大值优先 49 50 priority_queue<int,vector<int>,greater<int> >que3;//注意“>>”会被认为错误, 51 //这是右移运算符,所以这里用空格号隔开 52 priority_queue<int,vector<int>,less<int> >que4;////最大值优先 53 54 priority_queue<number1>que5; 55 priority_queue<number2>que6; 56 57 int i; 58 for(i=0;a[i];i++){ 59 que.push(a[i]); 60 que1.push(a[i]); 61 que2.push(a[i]); 62 que3.push(a[i]); 63 que4.push(a[i]); 64 } 65 for(i=0;num1[i].x;i++) 66 que5.push(num1[i]); 67 for(i=0;num2[i].x;i++) 68 que6.push(num2[i]); 69 70 71 printf("采用默认优先关系:\n(priority_queue<int>que;)\n"); 72 printf("Queue 0:\n"); 73 while(!que.empty()){ 74 printf("%3d",que.top()); 75 que.pop(); 76 } 77 puts(""); 78 puts(""); 79 80 printf("采用结构体自定义优先级方式一:\n(priority_queue<int,vector<int>,cmp>que;)\n"); 81 printf("Queue 1:\n"); 82 while(!que1.empty()){ 83 printf("%3d",que1.top()); 84 que1.pop(); 85 } 86 puts(""); 87 printf("Queue 2:\n"); 88 while(!que2.empty()){ 89 printf("%3d",que2.top()); 90 que2.pop(); 91 } 92 puts(""); 93 puts(""); 94 printf("采用头文件\"functional\"内定义优先级:\n(priority_queue<int,vector<int>,greater<int>/less<int> >que;)\n"); 95 printf("Queue 3:\n"); 96 while(!que3.empty()){ 97 printf("%3d",que3.top()); 98 que3.pop(); 99 } 100 puts(""); 101 printf("Queue 4:\n"); 102 while(!que4.empty()){ 103 printf("%3d",que4.top()); 104 que4.pop(); 105 } 106 puts(""); 107 puts(""); 108 printf("采用结构体自定义优先级方式二:\n(priority_queue<number>que)\n"); 109 printf("Queue 5:\n"); 110 while(!que5.empty()){ 111 printf("%3d",que5.top()); 112 que5.pop(); 113 } 114 puts(""); 115 printf("Queue 6:\n"); 116 while(!que6.empty()){ 117 printf("%3d",que6.top()); 118 que6.pop(); 119 } 120 puts(""); 121 return 0; 122 } 123 /* 124 运行结果 : 125 采用默认优先关系: 126 (priority_queue<int>que;) 127 Queue 0: 128 83 72 56 47 36 22 14 10 7 3 129 130 采用结构体自定义优先级方式一: 131 (priority_queue<int,vector<int>,cmp>que;) 132 Queue 1: 133 7 10 14 22 36 47 56 72 83 91 134 Queue 2: 135 83 72 56 47 36 22 14 10 7 3 136 137 采用头文件"functional"内定义优先级: 138 (priority_queue<int,vector<int>,greater<int>/less<int> >que;) 139 Queue 3: 140 7 10 14 22 36 47 56 72 83 91 141 Queue 4: 142 83 72 56 47 36 22 14 10 7 3 143 144 采用结构体自定义优先级方式二: 145 (priority_queue<number>que) 146 Queue 5: 147 7 10 14 22 36 47 56 72 83 91 148 Queue 6: 149 83 72 56 47 36 22 14 10 7 3 150 */ 151 运行结果: 152 采用默认优先关系: 153 (priority_queue<int>que;) 154 Queue 0: 155 83 72 56 47 36 22 14 10 7 3 156 采用结构体自定义优先级方式一: 157 (priority_queue<int,vector<int>,cmp>que;) 158 Queue 1: 159 7 10 14 22 36 47 56 72 83 91 160 Queue 2: 161 83 72 56 47 36 22 14 10 7 3 162 采用头文件"functional"内定义优先级: 163 (priority_queue<int,vector<int>,greater<int>/less<int> >que;) 164 Queue 3: 165 7 10 14 22 36 47 56 72 83 91 166 Queue 4: 167 83 72 56 47 36 22 14 10 7 3 168 采用结构体自定义优先级方式二: 169 (priority_queue<number>que) 170 Queue 5: 171 7 10 14 22 36 47 56 72 83 91 172 Queue 6: 173 83 72 56 47 36 22 14 10 7 3 174 好了,如果你仔细看完了上面的代码,那么你就可以基本使用优先队列了,下面给出一些我做题中有过的一些应用,希望能给大家带来一些启 175 示~ 176 1、先来一个我们最近做的题吧,http://acm.hdu.edu.cn/showproblem.php?pid=1242 177 题意:某人被关在囚笼里等待朋友解救,问能否解救成功,最少需要多少时间~ 178 具体:可同时有几个朋友,每走一格消耗一分钟的时间 ,地图上还存在着卫兵,卫兵可以解决掉,但是要另外花费一分钟~ 179 分析:从“a”出发,此题可以用回溯法进行深搜,但那样做的话,效率还是不能让人满意,但是广搜的话,由于入队后每次出队时,根据地 180 图情况的不同,出队元素所记忆的时间并不是层次递增的,因此使用简单广搜的话,同样需要全部搜索才能找到正确答案。有没有一种方法能 181 让某一步因为遇到士兵而多花时间的结点在队列中向后推迟一层出队呢?答案是肯定的,在这里我们可以用优先队列来实现,总体思想上是, 182 根据时间进行优先性选择,每次都要出队当前队列元素中记录时间最少的出队,而入队处理时,我们可以按顺序对四个方向上的各种情况按正 183 常处理入队就行了,出队顺序由优先队列根据预设优先性自动控制。这样,我们就可以从“a”进行基于优先队列的范围搜索了,并且在第一 184 次抵达有朋友的位置时得到正确结果~具体实现代码: 185 view plaincopy to clipboardprint? 186 /*HDU 1242 基于优先队列的范围搜索,16ms dooder*/ 187 188 #include<stdio.h> 189 #include<queue> 190 using namespace std; 191 192 #define M 201 193 typedef struct p{ 194 int x,y,t; 195 bool operator < (const p &a)const 196 { 197 return t>a.t;//取时间最少优先 198 } 199 }Point; 200 201 char map[M][M]; 202 Point start; 203 int n,m; 204 int dir[][2]={{1,0},{-1,0},{0,1},{0,-1}}; 205 206 int bfs() 207 { 208 priority_queue<Point>que; 209 Point cur,next; 210 int i; 211 212 map[start.x][start.y]='#'; 213 que.push(start); 214 while(!que.empty()){ 215 cur=que.top();//由优先队列自动完成出队时间最少的元素 216 que.pop(); 217 for(i=0;i<4;i++){ 218 next.x=cur.x+dir[i][0]; 219 next.y=cur.y+dir[i][1]; 220 next.t=cur.t+1; 221 if(next.x<0||next.x>=n||next.y<0||next.y>=m) 222 continue; 223 if(map[next.x][next.y]=='#') 224 continue; 225 if(map[next.x][next.y]=='r') 226 return next.t; 227 if(map[next.x][next.y]=='.'){ 228 map[next.x][next.y]='#'; 229 que.push(next); 230 } 231 else if(map[next.x][next.y]=='x'){ 232 map[next.x][next.y]='#'; 233 next.t++; 234 que.push(next); 235 } 236 } 237 } 238 return -1; 239 } 240 int main() 241 { 242 int i,ans; 243 char *p; 244 while(scanf("%d%d",&n,&m)!=-1){ 245 for(i=0;i<n;i++){ 246 scanf("%s",map[i]); 247 if(p=strchr(map[i],'a')){ 248 start.x=i; 249 start.y=p-map[i]; 250 start.t=0; 251 } 252 } 253 ans=bfs(); 254 printf(ans+1?"%d\n":"Poor ANGEL has to stay in the prison all his life.\n",ans); 255 } 256 return 0; 257 } 258 2、http://acm.hdu.edu.cn/showproblem.php?pid=1053 259 题意:给出一行字符串,求出其原编码需要的编码长度和哈夫曼编码所需的长度,并求其比值 260 分析:根据哈夫曼生成树的生成过程可知,其生成树的权值是固定的而且这个值是最小的,而且其值根据生成树的顺序,我们可以找出规律而 261 不需要真的去生成一棵树然后再求出权值,其模拟过程为取出队列中权值最小的两个元素,将其值加入结果中,然后将这两个元素的权值求和 262 即得出其父节点的权值,将生成元素作为结点入队~~如此循环,直至取出队列中最后两个元素加入结果,实现代码如下: 263 view plaincopy to clipboardprint? 264 /*HDU 1053 采用广搜求哈夫曼生成树的权值 0ms dooder*/ 265 #include<stdio.h> 266 #include<string.h> 267 #include<ctype.h> 268 #include<functional> 269 #include<queue> 270 using namespace std; 271 #define M 1000050 272 char str[M]; 273 int list[27]; 274 275 priority_queue< int,vector<int>,greater<int> >que; 276 277 int main() 278 { 279 int ans,sum; 280 int i,a,b,c; 281 while(scanf("%s",str),strcmp(str,"END")){ 282 memset(list,0,sizeof(list)); 283 for(i=0;str[i];i++){ 284 if(isalpha(str[i])) 285 list[str[i]-'A']++; 286 else 287 list[26]++; 288 } 289 sum=i*8;ans=i;c=0; 290 for(i=0;i<27;i++){ 291 if(list[i]){ 292 que.push(list[i]); 293 c++; 294 } 295 } 296 if(c>1){ans=0;//注意只有一种字符的情况 297 while(que.size()!=1){ 298 a=que.top(); 299 que.pop(); 300 b=que.top(); 301 que.pop(); 302 ans+=a+b; 303 que.push(a+b); 304 } 305 while(!que.empty())//使用后清空队列 306 que.pop(); 307 } 308 printf("%d %d %.1f\n",sum,ans,1.0*sum/ans); 309 } 310 return 0; 311 } 312 3、http://acm.pku.edu.cn/JudgeOnline/problem?id=2263 313 这是第二次练习赛时,我们做过的最后一题,这里采用优先队列进行实现,在《谁说不能这样做题》中已提到这种方法,在这里再次放出代 314 码,~ 315 题意:给出各城市间道路的限制载重量,求出从一个城市到另外一个城市的贷车能够运载的最大货物重量。 316 分析:采用优先队列,每次取出当前队列中结点的minheavy最大值出队,对它的连接结点搜索入队,这样,从出发点开始就可以 317 在到达终点时求出结果,即最大载货物重,实现代码如下: 318 view plaincopy to clipboardprint? 319 /*POJ 2263 16ms dooder*/ 320 321 #include<stdio.h> 322 #include<string.h> 323 #include<queue> 324 using namespace std; 325 #define M 201 326 typedef struct w{ 327 int city; 328 int mintons; 329 bool operator < (const w &a)const { 330 return mintons < a.mintons; 331 }//优先性定义 332 }Way; 333 char citys[M][31]; 334 int map[M][M]; 335 bool mark[M][M]; 336 int n,m,from,to,ans,k; 337 priority_queue <Way> que; 338 int min(int a,int b) 339 { 340 return a>b?b:a; 341 } 342 void bfs() 343 { 344 Way cur,next; 345 int i; 346 while(!que.empty()){ 347 cur=que.top(); 348 que.pop(); 349 if(cur.city==to){ 350 if(cur.mintons>ans) 351 ans=cur.mintons; 352 while(!que.empty()) 353 que.pop(); 354 return ; 355 } 356 for(i=0;i<n;i++){ 357 if(map[cur.city][i]&&!mark[cur.city][i]){ 358 next.city=i; 359 next.mintons=min(cur.mintons,map[cur.city][i]); 360 361 mark[cur.city][i]=mark[i][cur.city]=1; 362 que.push(next); 363 } 364 } 365 } 366 } 367 void run() 368 { 369 int i,temp,index; 370 Way cur; 371 ans=0; 372 memset(mark,0,sizeof(mark)); 373 temp=0; 374 for(i=0;i<n;i++){ 375 if(map[from][i]>temp){ 376 temp=map[from][i]; 377 index=i; 378 } 379 } 380 cur.city=index; 381 cur.mintons=temp; 382 que.push(cur); 383 bfs(); 384 } 385 int main() 386 { 387 int k1,k2,tons,t=1; 388 char s1[31],s2[31]; 389 while(scanf("%d%d",&n,&m),n||m){ 390 k=0; 391 while(m--){ 392 scanf("%s%s%d",s1,s2,&tons); 393 for(k1=0;strcmp(s1,citys[k1])&&k1<k;k1++); 394 if(k1==k) 395 strcpy(citys[k++],s1); 396 for(k2=0;strcmp(s2,citys[k2])&&k2<k;k2++); 397 if(k2==k) 398 strcpy(citys[k++],s2); 399 map[k1][k2]=map[k2][k1]=tons; 400 } 401 scanf("%s%s",s1,s2); 402 for(from=0;strcmp(citys[from],s1);from++); 403 for(to=0;strcmp(citys[to],s2);to++); 404 run(); 405 printf("Scenario #%d\n",t++); 406 printf("%d tons\n\n",ans); 407 } 408 return 0; 409 } 410 当然了,优先队列的用法决不是仅仅提到的这些,各种应用还需要大家去发现,给道题大家可以练习一下hdu 2066\ 411 相信大家已经学到不少了,还有一点可以告诉大家,优先队列是启发式搜索的数据结构基础,希望好好理解,并逐步掌握其用法~ 412 加:失策啊,竟然忘了说优先队列的效率了,其时间复杂度为O(logn).n为队列中元素的个数,存取都需要消耗时间~
作 者:Angel_Kitty
出 处:https://www.cnblogs.com/ECJTUACM-873284962/
关于作者:阿里云ACE,目前主要研究方向是Web安全漏洞以及反序列化。如有问题或建议,请多多赐教!
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信我
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是作者坚持原创和持续写作的最大动力!
欢迎大家关注我的微信公众号IT老实人(IThonest),如果您觉得文章对您有很大的帮助,您可以考虑赏博主一杯咖啡以资鼓励,您的肯定将是我最大的动力。thx.
我的公众号是IT老实人(IThonest),一个有故事的公众号,欢迎大家来这里讨论,共同进步,不断学习才能不断进步。扫下面的二维码或者收藏下面的二维码关注吧(长按下面的二维码图片、并选择识别图中的二维码),个人QQ和微信的二维码也已给出,扫描下面👇的二维码一起来讨论吧!!!
欢迎大家关注我的Github,一些文章的备份和平常做的一些项目会存放在这里。