什麼叫優先隊列呢,能完成以下任務的就叫做優先隊列:
·插入一個數值
·取出最小的數值(獲取數值,並且刪除)
實現優先隊列,應該使用二叉樹完成,是一種叫二叉堆的數據結構(binary heap)
二叉堆分爲兩種,最小堆和最大堆。最小堆是父節點的鍵值總是小於等於子節點的鍵值。最大堆是父節點的鍵值總是大於等於子節點的鍵值。
可以將二叉堆看成數組的形式。
代碼:
// 模擬最小堆
// 最小堆是二叉堆的一種,其特點是父節點的鍵值總是小於或者等於子節點。
// 實現細節:
// push:向堆中插入數據時,首先在堆的末尾插入數據,然後不斷向上提升,直到沒有大小顛倒時。
// pop:從堆中刪除最小值時首先把最後一個值複製到根節點上,並且刪除最後一個數值。然後不斷向下交換
// 直到沒有大小顛倒爲止。在向下交換過程中,如果有兩個子兒子都小於自己,就選擇較小的
#include <iostream>
using namespace std;
const int MAX_N = 1005;
int heap[MAX_N], sz = 0;
void push(int x);
void display();
int pop();
int main()
{
// 測試
int x;
int cmd;
do
{
cout << "請輸入命令:1.push\t2.pop\t3.display\t0.quit\n";
cin >> cmd;
switch(cmd)
{
case 1:
cout << "Input X:";
cin >> x;
push(x);
break;
case 2:
x = pop();
cout << x << "已取出!\n";
break;
case 3:
display();
break;
}
}while(cmd);
return 0;
}
void push(int x)
{
// i是要插入節點的下標
int i = sz++;
while(i > 0)
{
// p爲父親節點的下標
int p = (i - 1) / 2;
// 如果父親節點小於等於插入的值,則說明大小沒有跌倒,可以退出
if(heap[p] <= x)
break;
// 互換當前父親節點與要插入的值
heap[i] = heap[p];
i = p;
}
heap[i] = x;
cout << "數據插入成功!\n";
}
int pop()
{
// 取出根節點
int ret = heap[0];
// 將最後一個節點的值提到根節點上
int x = heap[--sz];
int i = 0;
while(i * 2 + 1 < sz)
{
// a,b爲左右兩個子節點的下標
int a = 2 * i + 1, b = 2 * i + 2;
// 去兩個子節點中較小的值
if(b < sz && heap[b] < heap[a])
a = b;
// 如果已經沒有大小顛倒的話則退出循環
if(heap[a] >= x)
break;
// 將父親節點與子節點互換
heap[i] = heap[a];
i = a;
}
heap[i] = x;
return ret;
}
void display()
{
for(int i = 0; i < sz; i++)
cout << heap[i] << "\t";
cout << endl;
}
但是在c++的STL中已經包含了優先隊列的高效實現——priority_queue,不過與上面的例子不一樣的是每次取出數值都是最大值。
看一個簡單的實例:
#include <iostream>
#include <queue>
using namespace std;
int main()
{
// 聲明
priority_queue<int> pque;
// 插入元素
pque.push(3);
pque.push(5);
pque.push(1);
// 不斷循環直到爲空爲止
while(!pque.empty())
{
// 獲得最大值並且刪除
cout << pque.top() << endl;
pque.pop();
}
return 0;
}
用杭電的一題來實戰一下吧
題目點擊打開鏈接
題目的意思是和走迷宮找最短出口差不多,就是有一個天使(在地圖上顯示爲a),你是天使的好基友(在地圖上顯示爲r),爲了拯救基友,你打算去劫獄,.代表路, #代表牆壁。每走一步就要花一個時間單位。監獄裏當然會有很多獄警(在地圖上顯示爲x)啦,但是基情使你充滿力量,輕輕鬆鬆能殺死一個獄警,也只要一個時間單位好了。現在你要怎麼做才能在最短的時間內救出好基友。
=_=
這題很明顯就是使用BFS來做的。因爲裏面多了一個獄警,所以用隊列的話就比較麻煩了,所以在這裏使用優先隊列。
因爲優先隊列默認是取出最大值的,所以首先要重載一下運算符,讓優先取出最小值。
ac代碼如下:
#include <iostream>
#include <queue>
#include <string>
using namespace std;
const string fail = "Poor ANGEL has to stay in the prison all his life.";
const int INF = 1000005;
const int MAX_N = 205;
int N, M;
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
char pic[MAX_N][MAX_N];
struct Node
{
int x, y;
int num;
// 將優先隊列每次取出最大值改爲每次去最小值
bool operator < (const Node &a) const
{
return num > a.num;
}
}first, next;
int bfs();
int main()
{
while(cin >> N >> M)
{
int i, j;
for(i = 0; i < N; i++)
{
cin >> pic[i];
for(j = 0; j < M; j++)
{
if(pic[i][j] == 'a')
{
first.x = i;
first.y = j;
first.num = 0;
pic[i][j] = '#';
}
}
}
int ans = bfs();
if(ans == INF)
cout << fail << endl;
else
cout << ans << endl;
}
return 0;
}
int bfs()
{
priority_queue<Node> que;
que.push(first);
while(que.size())
{
first = que.top();
que.pop();
for(int i = 0; i < 4; i++ )
{
next.x = first.x + dx[i];
next.y = first.y + dy[i];
if(next.x >= 0 && next.x < N && next.y >=0 && next.y < M && pic[next.x][next.y] != '#')
{
next.num = first.num;
if(pic[next.x][next.y] == 'r')
return next.num + 1;
else if(pic[next.x][next.y] == '.')
next.num += 1;
else if(pic[next.x][next.y] == 'x')
next.num += 2;
pic[next.x][next.y] = '#';
que.push(next);
}
}
}
return INF;
}