目录
过程分析
代码实现
queue_maze.c
上节我们讲完了队列,本节开始学习广度优先算法!
之前我们用深度优先算法找出从迷宫出来的所有路径,本次我们要用广度优先算法找出最短路径。
过程分析
广度优先算法也叫广度优先搜索。
这种算法就相当于是人有分身术,在起点的位置,人可以往右边那条路径走,分身可以往下面那条路径走,每人一步,在移动的过程中如果再次遇到分叉口,就可以再次分身,谁先到达终点,哪个路径就是最短路径。
显然我们需要一个东西来记录路径,这个东西就是循环队列。
然后我们就分析这个起点接下来是往右走,还是往下走,这时我们需要让这个点出队(每一个点出队就让队头指针往后移一个)。
每走一个点就让它进队,遇到岔道口就让它出队,分析它该往哪里走。
分析它下一步的方向该往哪里走,我们也和上次一样规定四个方向,0是向上,1是向右,2是向下,3是向左。
比如我们现在是在起点,向上不能走,向左也不能走,向右可以,我们就把向右的这个点入队,进队操作的是队头(即把点放在此时队头所指向的位置)
进队结束后应该把队尾往后移一个
回来再看,在起点的位置向下也能走,所以把向下走的这个点进队
到这里,起点的这个已经分析完了,但是还必须得让它保留在这个数组里面,即还在这块内存里面,因为打印路径的路径的时候要用到。
这个点分析完之后,我们继续分析(0,1)这个点,所以让这个点出队(让队头指针向后移一个)
这个点只能向右走,于是把能走的这个点(0,2)进队
接下来分析(1,0)的这个点,让它出队,它只能往下走,于是让(2,0)这个点进队
接下来分析(0,2)这个点,让它出队,它可以往下走,也可以往右走
......
分析到这里我们已经知道这其实是多条路径同时进行的,谁先到达终点,说明谁走过的路径就是多短路径。
这个过程什么时候结束呢?
当最后出队的点(即要分析的点)是终点的时候这个过程就结束了。
当我们知道谁走过的那条路是最短路径的时候,怎么打印出来呢?
比如我们知道走过(2,0)这个点对应的是最短路径,我们就可以顺着记录下来的下标找到它前面点,依次倒推,就能得到一条完整的路径
下面开始写代码
代码实现
queue_maze.c
#include <stdio.h>
//定义一个点
typedef struct Box
{
int x;//点的行坐标
int y;//点的列坐标
int pre;//前面那个点在数组(队列)中的下标
}Box;
typedef struct Queue
{
Box data[1024];//点
int front;//队头
int rear;//队尾
}Queue;
//定义一个二维数组
int map[6][6] = {
{0, 0, 0, 0, 0, 0},
{0, 1, 0, 1, 1, 0},
{0, 1, 0, 0, 1, 0},
{0, 1, 1, 0, 1, 0},
{0, 1, 1, 0, 0, 1},
{0, 0, 0, 0, 0, 0}
};
//初始化队列函数
void InitQueue(Queue*q)
{
q->front=q->rear=0;
}
//进队函数
void push(Queue *q,Box b)
{
//把元素(点)放在队尾这个地方,同时队尾向后移动一个
q->data[q->rear++]=b;
}
//出队函数
void Pop(Queue*q, Box*b)
{
//先把点记录下来,再出队(front++)
*b=q->data[q->front++];
}
//判断空队
int EmptyQueue(Queue *q)
{
//队头和队尾重合就是空队
return (q->front==q->rear) ? 1:0;
}
//打印路径函数
void ShowPath(Queue *q,int end)
{
//顺着我们记下来的pre先从后往前,把走过的标为-1,然后再从前往后走打印出来
int i,tmp;
for(i=end-1;i>=0;)
{
tmp=q->data[i].pre;//把pre存下来
q->data[i].pre=-1;//然后再改成-1
i=tmp;//tmp正好是上一个点的下标
}
for(i=0;i<end;i++)
{
if(q->data[i].pre==-1)
{
printf("[%d %d]-->",q->data[i].x,q->data[i].y);
}
}
printf("\n");
}
//走迷宫函数
int Walk(Queue*q,int x1,int y1,int x2,int y2)
{
//创建起点和下一个点
Box now,next;
//初始化起点
now.x=x1;
now.y=y1;
now.pre=-1;
//起点进队
push(q,now);//把队列和点传过去
//标记走过的点
map[now.x][now.y]=-1;
while(EmptyQueue(q)!=1)//只要队列不为空就继续循环
{
//分析某个点,让某个点出队
Pop(q,&now);
if(now.x==x2 && now.y==y2)//最后出队的是终点
{
//打印路径
ShowPath(q,q->front);
return 1;//结束程序
}
//如果不是终点就需要分析它接下来可以走的方向
int dir;//0向上,1向右,2向下,3向右
for(dir=0;dir<4;dir++)
{
switch(dir)
{
case 0:
next.x=now.x-1;
next.y=now.y;
break;
case 1:
next.x=now.x;
next.y=now.y+1;
break;
case 2:
next.x=now.x+1;
next.y=now.y;
break;
case 3:
next.x=now.x;
next.y=now.y-1;
break;
}
//要知道某个方向能不能走需要先把它的坐标求出来有没有超范围和条件
if(next.x>=0 && next.x<=5 && next.y>=0 && next.y<=5 && map[next.x][next.y]==0)
{
//把“从哪里过来的点”记录下来
next.pre=q->front-1;
//如果点可以走就可以让它进队
push(q,next);
//标记走过了
map[next.x][next.y]=-1;
}
}
}
return 0;
}
int main()
{
//创建队列
Queue queue;
//初始化队列
InitQueue(&queue);
//走迷宫
if(Walk(&queue,0,0,5,5)==0)//队列 起点坐标 终点坐标
{
printf("路径不存在\n");
}
return 0;
}
运行结果
这条路径先到达终点
下节开始学习二叉树!
QQ交流群:963138186
本篇就到这里,下篇继续!欢迎点击下方订阅本专栏↓↓↓