在上一篇中我们通过深度优先搜索找到迷宫从入口到终点的最短路径,这次我们换一种思路,通过一层一层扩展的方法来找到终点,扩展时每发现一个点就将这个点加入到队列中,直至走到终点位置为止,即广度优先搜索(BFS)。
算法图解
依然是以下面这个迷宫为例,我们需要找到从起点到终点的最短路径。
广度优先的思想就是遍历每一步能到达的所有位置,判断这些位置是否为终点,如果都不是就找到下一步能到达的所有位置,直到到达终点。例如起点处能到达(1,2)和(2,1),都不是终点,那么我们继续看这两个点能到达(2,2),(3,1),(2,3),仍然没有到达终点,继续向前找,如下:
回顾刚才的算法,我们可以用一个队列来模拟,队列中通过结构体来存储我们需要的信息。
struct note
{
public int x; //横坐标
public int y; //纵坐标
public int f; //上一步在队列中的编号
public int s; //步数
}
//队列初始化
head = tail = 1;
//将起点坐标加入到队列
startx = starty = 1;
que[tail] = new note()
{
x = startx,
y = starty,
f = 0,
s = 0
};
tail++;
然后从(1,1)开始,先尝试右走到达(1,2)。
//计算下一个点的坐标
tx = que[head].x + next[k][0];
ty = que[head].y + next[k][1];
判断(1,2)是否越界
if (tx < 1 || tx > n || ty < 1 || ty > m)
{
continue;
}
再判断(1,2)是否为障碍物或者已经在路径中。
if (a[tx][ty] == 0 && book[tx][ty] == 0)
{
}
如果满足上面的条件,则将(1,2)入队,并标记该点已经走过。
//把这个点标记为已经走过
book[tx][ty] = 1;
//把新的点加到队列中
que[tail].x = tx;
que[tail].y = ty;
que[tail].f = head; //这一步是从head处扩展的
que[tail].s = que[head].s + 1; //步数是上一步的步数+1
tail++;
然后继续尝试其它方向走,我们发现(1,1)还可以到达(2,1),因此再将(2,1)也加入队列,代码实现和刚才对(1,2)的操作一样。
对(1,1)扩展完后,(1,1)就可以出队了。出队只需要将head指向的位置向前加一即可。
head++;
按照上面的思路不断扩展直至找到终点为止。完整代码实现如下:
class Program
{
static void Main(string[] args)
{
int i, j, startx, starty, head, tail, flag, tx, ty;
//初始化迷宫信息5*4
n = 5;
m = 4;
for (i = 1; i <= n; i++)
{
a[i] = new int[5];
book[i] = new int[5];
for (j = 1; j <= m; j++)
{
//(1,3),(3,3),(4,2),(5,4)处为障碍物
if ((i == 1 && j == 3) || (i == 3 && j == 3) || (i == 4 && j == 2) || (i == 5 && j == 4))
{
a[i][j] = 1;
}
else
{
a[i][j] = 0;
}
}
}
//终点为(4,3)
p = 4;
q = 3;
//队列初始化
head = tail = 1;
//将起点坐标加入到队列
startx = starty = 1;
que[tail] = new note()
{
x = startx,
y = starty,
f = 0,
s = 0
};
tail++;
book[startx][starty] = 1;
flag = 0; //用来标记是否到达目的点,0表示暂时还没到达
//当队列不为空时循环
while (head < tail)
{
//遍历四个方向
for (int k = 0; k < next.Length; k++)
{
//计算下一个点的坐标
tx = que[head].x + next[k][0];
ty = que[head].y + next[k][1];
//判断是否越界
if (tx < 1 || tx > n || ty < 1 || ty > m)
{
continue;
}
//判断该点是否为障碍物或者已经在路径中
if (a[tx][ty] == 0 && book[tx][ty] == 0)
{
//把这个点标记为已经走过
book[tx][ty] = 1;
//把新的点加到队列中
que[tail].x = tx;
que[tail].y = ty;
que[tail].f = head; //这一步是从head处扩展的
que[tail].s = que[head].s + 1; //步数是上一步的步数+1
tail++;
}
//如果找到终点,停止扩展,任务结束,退出循环
if (tx == p && ty == q)
{
flag = 1;
break;
}
}
if (flag == 1)
{
break;
}
head++;//当一个点扩展结束后,head++才能对后面的点再进行扩展
}
//tail指向的是队列队尾的下一个位置,所以此处需要-1
Console.WriteLine("最短步数为:" + que[tail - 1].s);
Console.ReadKey();
}
static int n, m, p, q;
static int[][] a = new int[6][]; //记录障碍物
static int[][] book = new int[6][];//记录走过的路径
static note[] que = new note[25];
static int[][] next = new int[][]
{
new int[] { 0, 1 }, //向右
new int[] { 1, 0 }, //向下
new int[] { 0, -1 },//向左
new int[] { -1, 0 } //向上
};
struct note
{
public int x; //横坐标
public int y; //纵坐标
public int f; //上一步在队列中的编号
public int s; //步数
}
}
最终执行结果和深度优先搜索一样,最短步数为7,如下:
上一篇:深度优先搜索解决迷宫最短路径问题