学习 2018-10-13 16:262160阅读 · 48喜欢 · 3评论
粉丝:1094文章:14
关注
求解迷宫问题有两种基本思路,深度优先与广度优先。这次介绍其中一种:广度优先
在此之前不妨先介绍一下什么是深度优先,什么是广度优先
想想一下自己现在正处于迷宫之中,面前有几条岔路(该迷宫不止一条正确路径)
深度优先:
你觉得只要能出去就行,于是决定一条一条路去试,但是瞎jb试容易把自己搞糊涂,所以你制定了一条规则,一直沿着右手边那条路走(当然如果你觉得用左手更舒服也可以选择左边),直到遇见死胡同,拐回最近的岔路口,重复这样的行为,当然,已经走过的路就不必再走一遍了。这样你很开心,因为你可以遍历所有的岔路而不必担心忘记哪条路走过哪条路没走。这样一来,你迟早会找到出口,但这样的缺点是什么呢,当你站在出口处回首迷宫,你会发现,因为一味的沿右手走,你很可能因此走了很多弯路,换句话说,这并不是最短的路线。
广度优先:
你作为一个强迫症,决定一定要找到最短的出迷宫的路线,所以站在岔路口的你选择先将所有岔路放入“备选项”。而你走过的路是不能再经过备选项的,因为显然如果那样的话直接走备选项岂不是少走很多路?举个例子
这是一个迷宫,入口在1,出口在3,如果你选择深度优先并且很不幸的选择了左手,那么你的路线是1->2->4->3,你走了4个格子终于找到了出口
但当你选择广度优先呢?
当你还在1的时候就将2和3放入了备选项,然后你走到了2,这时候只有4能进到你的备选项,但当你走到4的时候发现:嗯?3已经在备选项中了,那我干嘛不直接从1走到3?于是,最优解诞生了(鼓掌)。
但是广度优先也有缺点,其中之一就是因为要记忆很多备选项,自然占用的脑容量就要很多。
所以,选择深度优先还是广度优先还是需要根据特定情况进行抉择。
废话了这么多来说一下算法设计思路
这是我们的迷宫
我们需要一个队列存储所有备选项
队列中充斥着结构体Box,Box即是迷宫中的一个个单元格,Box中有一个int型pre,用来记录该Box的来时的Box在该队列中的位置,比如对于线路Box1->Box2来说,Box2中存储了Box1在队列中的位置
入口方块进队,队列不为空时循环
循环体内:
出队一个方块e,找e周围所有可以走的方块,假设有e1,e2,将它们两个进队,pre均设置为front
直到e的坐标与出口坐标相等,即为找到了出口,通过pre逆向查找至入口,得出最短路径,正向打印.
源码:
头文件Queue.h:
#pragma once
#define MaxSize 100
typedef struct
{
int i, j; //方块在地图中的位置
int pre; //该路径中上一个方块在队列中的下标
}Box; //方块类型
typedef struct
{
Box data[MaxSize];
int front, rear;
}Queue; //用于存放路径的队列
void InitQueue(Queue *& q)
{
q = (Queue *)malloc(sizeof(Queue));
q->front = q->rear = -1;
}
void DestroyQueue(Queue *&q)
{
free(q);
}
bool QueueEmpty(Queue *q)
{
return (q->front == q->rear);
}
bool enQueue(Queue *& q, Box e)
{
if (q->rear == MaxSize - 1)
return false;
q->rear++;
q->data[q->rear] = e;
return true;
}
bool deQueue(Queue *& q, Box & e)
{
if (q->rear == q->front)
return false;
q->front++;
e = q->data[q->front];
return true;
}
cpp:
#include <iostream>
#include "Queue.h"
#define M 8
#define N 8
using namespace std;
int map[M + 2][N + 2] = { //地图,1为不可走,0为可走
{1,1,1,1,1,1,1,1,1,1},{1,0,0,1,0,0,0,1,0,1},{1,0,0,1,0,0,0,1,0,1},{1,0,0,0,0,1,1,0,0,1},{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},{1,0,1,0,0,0,1,0,0,1},{1,0,1,1,1,0,1,1,0,1},{1,1,0,0,0,0,0,0,0,1},{1,1,1,1,1,1,1,1,1,1}
};
void ShowPath(Queue *qu, int front) //输出正确路径
{
int p = front, p0;
do
{
p0 = p;
p = qu->data[p].pre;
qu->data[p0].pre = -1;//将正确路径的pre记为-1用以标记
} while (p != 0); //利用循环反向找出正确路经
cout << "最短路径:" << endl;
for (int k = 0; k < MaxSize; k++)
{
if (qu->data[k].pre == -1)
{
cout << "(" << qu->data[k].i << "," << qu->data[k].j << ")";
cout << "->";
}
}
}
bool Path(int x0, int y0, int x, int y) //广度优先找最优解
{
int i, j, i0, j0; //i,j存储当前方块位置,i0,j0存储找到新路径的位置
Box e; //当前方块
Queue * qu;
InitQueue(qu);
e.i = x0; //设置起点位置
e.j = y0;
e.pre = -1; //起点pre设置为-1;
enQueue(qu, e); //起点入队
map[x0][y0] = -1; //走过的点记为-1,表示不可再走
while (!QueueEmpty(qu)) //队非空时循环
{
deQueue(qu, e); //出队,用e存储
i = e.i; //记录该点坐标
j = e.j;
if (i == x && j == y)
{
ShowPath(qu, qu->front); //打印路径
DestroyQueue(qu);
return true;
}
for (int circle = 0; circle < 4; circle++) //遍历周围的方块,若方块可走就将其进队
{
switch (circle) //遍历顺序为:上,右,下,左
{
case 0:
i0 = i - 1;
j0 = j;
break;
case 1:
i0 = i;
j0 = j + 1;
break;
case 2:
i0 = i + 1;
j0 = j;
break;
case 3:
i0 = i;
j0 = j - 1;
break;
}
if (map[i0][j0] == 0) //移动至新位置
{
e.i = i0;
e.j = j0;
e.pre = qu->front; //用pre记录队列的数据下标
enQueue(qu, e);
map[i0][j0] = -1;
}
}
}
DestroyQueue(qu); //若队为空,说明遍历所有可以到达的方块后依旧找不到出口,即为无解
return false;
}
int main()
{
if (!Path(1, 1, 8, 8))
cout << "迷宫无正确路径";
return 0;
}
结果: