【问题描述】
【问题分析】
迷宫问题是一个
5
×
5
5 × 5
5×5的二维数组,搜索起来不会很复杂,也不会超时。从左上角(0, 0)位置开始,上下左右进行搜索,可以定义一个方向数组,代表上下左右四个方向,使用方向数组,可以使一个点上下左右移动。对于数组中的每个元素用结构体来存储,除了有x,y成员外,还要定义pre成员,用来表示从左上角到右下角的最短路径中每个元素的前一个元素的下标,即保存路径,方便后面的输出。通过广度搜索借助队列进行操作,当中要注意1.搜索的元素是否在迷宫内;2.是否可行(其中1是墙壁不能走,0可以走)。我们可以把已走过的路标记为1,这样能保证路径最短(因为广度优先搜索,只会离初始点的步数一步步增多。如果之后发现遇到了已走过的点,那肯定是从之前已走过的点那边走最短,而不是从之后发现的点走最短啊),直到搜索到右下角(4, 4),问题就解决了。输出路径即可。
搜索的方向定义为
(
0
,
1
)
−
>
(
0
,
−
1
)
−
>
(
1
,
0
)
−
>
(
−
1
,
0
)
(0, 1) -> (0, -1) -> (1, 0) -> (-1, 0)
(0,1)−>(0,−1)−>(1,0)−>(−1,0)也即右,左,下,上
根据样例的数据可以得到以下的过程分析:
注意:置1可以将该位置表示为访问过:
- ( 0 , 0 ) , p r e = − 1 , r e a r = 1 , f r o n t = 0 (0, 0), pre = -1, rear = 1, front = 0 (0,0),pre=−1,rear=1,front=0 初始位置,找了一圈之后发现可以往下即 ( 1 , 0 ) (1, 0) (1,0) 出队列
- ( 1 , 0 ) , p r e = 0 , r e a r = 2 , f r o n t = 1 (1, 0), pre = 0, rear = 2, front = 1 (1,0),pre=0,rear=2,front=1 进队列,搜索查找 -> (2, 0)
- ( 2 , 0 ) , p r e = 1 , r e a r = 3 , f r o n t = 2 (2, 0), pre = 1, rear = 3, front = 2 (2,0),pre=1,rear=3,front=2 此时再进行搜索会出现两种情况,向下和向右
- ( 2 , 1 ) , p r e = 2 , r e a r = 4 , f r o n t = 3 (2, 1), pre = 2, rear = 4, front = 3 (2,1),pre=2,rear=4,front=3 对应于向右的情况,
- ( 3 , 0 ) , p r e = 2 , r e a r = 5 , f r o n t = 4 (3, 0), pre = 2, rear = 5, front = 4 (3,0),pre=2,rear=5,front=4 对应于向下的情况
- ( 2 , 2 ) , p r e = 3 , r e a r = 6 , f r o n t = 5 (2, 2), pre = 3, rear = 6, front = 5 (2,2),pre=3,rear=6,front=5 (2,1)往右走
- ( 4 , 0 ) , p r e = 4 , r e a r = 7 , f r o n t = 6 (4, 0), pre = 4, rear = 7, front = 6 (4,0),pre=4,rear=7,front=6 (3,0)往下
- ( 2 , 3 ) , p r e = 5 , r e a r = 8 , f r o n t = 7 (2, 3), pre = 5, rear = 8, front = 7 (2,3),pre=5,rear=8,front=7 (2,2)往右
- ( 4 , 1 ) , p r e = 6 , r e a r = 9 , f r o n t = 8 (4, 1), pre = 6, rear = 9, front = 8 (4,1),pre=6,rear=9,front=8 (4,0)往右
- ( 2 , 4 ) , p r e = 7 , r e a r = 10 , f r o n t = 9 (2, 4), pre = 7, rear = 10, front = 9 (2,4),pre=7,rear=10,front=9 (2, 3)往右
- ( 4 , 2 ) , p r e = 8 , r e a r = 11 , f r o n t = 10 (4, 2), pre = 8, rear = 11, front = 10 (4,2),pre=8,rear=11,front=10 (4, 1)往右
- ( 3 , 4 ) , p r e = 9 , r e a r = 12 , f r o n t = 11 (3, 4), pre = 9, rear = 12, front = 11 (3,4),pre=9,rear=12,front=11 (2, 4)往下
- ( 4 , 4 ) , p r e = 11 , r e a r = 13 , f r o n t = 12 (4, 4), pre = 11, rear = 13, front = 12 (4,4),pre=11,rear=13,front=12 (3, 4)往右 pathX和pathY的结果都是4,直接打印就行
【算法设计】
/*
* 迷宫问题
*/
#include <iostream>
using namespace std;
const int n = 5;
int maze[n][n] = {0};
//相邻的四个节点横纵坐标
int borderX[4] = {0, 0, 1, -1};
int borderY[4] = {1, -1, 0, 0};
//模拟队列的头尾
int front = 0;
int rear = 1;
//节点
struct node
{
int pre; //前驱节点
int x; //横坐标
int y; //纵坐标
}path[100]; //记录路径
//打印节点
void print(int i) //当前节点
{
if(path[i].pre != -1) //找到前面那个节点
{
print(path[i].pre);
cout << "(" << path[i].x << ", " << path[i].y << ")" << endl;
}
else //最前面的节点,无需递归,直接打印即可
{
cout << "(" << path[i].x << ", " << path[i].y << ")" << endl;
}
}
void bfsSearch(int x, int y)
{
//从开始节点出发
path[front].x = x;
path[front].y = y;
path[front].pre = -1; //回溯终点
maze[x][y] = 1; //标记走过
//front == rear是已经走完且都不通
while(front < rear) //队列不空
{
for(int i=0; i<4; i++)
{
//相邻节点的坐标
int pathX = path[front].x + borderX[i];
int pathY = path[front].y + borderY[i];
//不符合的节点(遇到边界或者已经走过了)
if(pathX<0 || pathY<0 || pathX>4 || pathY>4 || maze[pathX][pathY]) //1表示走不了
continue;
else //可以走的情况,将其加入到队列中
{ //将front的相邻的可以过去的并且还是没有走过的节点加到路径中
maze[pathX][pathY] = 1; //标记已经走过了
path[rear].x = pathX;
path[rear].y = pathY;
path[rear].pre = front; //标记当前的前趋
rear++;
}
if(pathX == 4 && pathY == 4)
{
//找到了一条路径
print(rear - 1);
break;
}
}
front++; //出队列,看下一个元素
}
}
int main()
{
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
cin >> maze[i][j];
}
}
bfsSearch(0, 0);
return 0;
}
【算法分析】
可供参考的文章: