您的位置:首页 > 其它

优先队列和堆 实战:HDU1242

2016-03-20 14:20 190 查看
什么叫优先队列呢,能完成以下任务的就叫做优先队列:

·插入一个数值

·取出最小的数值(获取数值,并且删除)

实现优先队列,应该使用二叉树完成,是一种叫二叉堆的数据结构(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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: