BFS和顺序队列求最短路径

数据结构的课程开始也有一段时间了,第一次实验作业中,有这样一道题:
大意是说给定两个数轴上的点M,N.限定M的移动方式有3种:
①:左移一步:即Mnew=M-1;
②:右移一步:即Mnew=M+1;
③:右跳一步:即Mnew=M*2;
问M最少移动几次可以到达N;
初次看题我没想到用哪种数据结构,自己学得慢…与老师无瓜.因为之前用栈做了前缀表达式的题,还
试了一个迷宫的题,发现它的结构很适合用回溯法,因为先进后出,只要弹出栈顶就回到上一步了。
于是我想这题可不可以按迷宫的做法来做,看上去也是寻找路径问题,并且还简化了许多。此时,我
还没明白的是栈的那种搜索其实是递归的,并且是dfs搜索,它可能找到解,但未必是最优解,方向就
偏了。苦苦挣扎了一夜,死在递归上,多亏有人点醒,我又去翻书看队列,不禁后悔思索题目时没有
往后翻翻书,因为当时我已经会写顺序,循环,链式的队列了,但还没有看书上例题。结果一看,队列
居然也可以用来解迷宫。了解到它是属于bfs搜索后,大致了解了一下bfs能求得的是最优解,对于求最
短路径很有用的时候,我才恍然大悟。仿照例程写出了题解:

#include 
#include 
#define Maxsize 100
typedef struct
{
    int pos;
    int pre;
}Box;
typedef Box ElementType;
typedef struct
{
    Box data[Maxsize];
    int front,rear;
}Queue;
Queue *CreateOrdQue()
{
    Queue *qu;
    qu=(Queue*)malloc(sizeof(Queue));
    qu->front=qu->rear=-1;
    return qu;
}
int isEmpty(Queue* q)
{
    return (q->front==q->rear);
}
int EnQueue(Queue *q,ElementType e)
{
    if(q->rear==Maxsize-1)//队满
        return 0;
    else{
        q->rear++;
        q->data[q->rear]=e;
        return 1;
    }
}
int DeQueue(Queue *q,ElementType *e)
{
    if(isEmpty(q))
        return 0;
    else
    {
        q->front++;
        *e=q->data[q->front];
        return 1;
    }
}
int bfs(int start,int end)
{
    int step[end+1];//记录走没走过
    for(int i=0;i<end+1;i++)
        step[i]=0;//初始化
    Box e;//记录路径和当前位置
    int st,cst;//当前位置和探索的位置
    int di;//遍历每层
    Queue * qu;
    qu=CreateOrdQue();//初始化队列
    e.pre=-1;e.pos=start;//pos记录的是上一个位置
    EnQueue(qu,e);//第一个进队
    step[start]=-1;//标记起点走过
    while(!isEmpty(qu))
    {
        DeQueue(qu,&e);
        st=e.pos;
        if(st==end)//找到的话
        {
            print(qu,qu->front);//输出路径
            free(qu);//释放空间
        return 1;
        }
        for(di=0;di<3;di++)//cxk的三种走法
        {
            switch(di)
            {
                //左移
                case 0:cst=st-1;break;
                case 1:cst=st+1;break;//右移
                case 2:cst=st*2;break;//跳步
            }
            if(step[cst]==0)//尚未走过的话
                {
                    e.pos=cst;
                    e.pre=qu->front;//记录前一个位置坐标
                    EnQueue(qu,e);//将下一个位置入队
                    step[cst]=-1;//并标记已经走过
                }
        }
    }
        free(qu);
        return 1;
}
void print(Queue *qu,int front)//因为是顺序队列,元素还在数组里,逆向寻找输出
{
    int count=0;
    int k,j;
    int control=0;
    k=front;
    printf("\n");
    do
    {
        j=k;
        k=qu->data[k].pre;
        qu->data[j].pre=-1;//反向走并标记走过的
    }while(k!=0);//顺序队列,第一个位置在0
    printf("路径为:\n");
    while(k<Maxsize)//遍历整个队列,因为正确的路径标记为-1
    {
        if(qu->data[k].pre==-1)
        {
            control++;
            printf("\t%d",qu->data[k].pos);
            count ++;
            if(control%5==0) printf("\n");
        }
        k++;
    }
    printf("\n总共走 %d步",count-1);
}
int main()
{
    int n,m;
    printf("输入位置m,和位置n:");
    scanf("%d %d",&m,&n);
    if(!bfs(m,n))
        printf("达不到");
    return 1;
    return 0;
}

首先我们需要一个顺序队列,刚开始我不太明白为什么不用循环队列,就上网查bfs可不可以用循环队列实现,未果。在写的过程中我意识到,如果是循环队列的话,当数组大小MaxSize不合适的话:比如为
50,但是M和N分别接近数组两端,此时在搜索过程中,会覆盖走过的路径,最后我们是输出不了找到的路径的。大概是这个原因吧,希望有人批评指正。
好了,再说程序,顺序队列的数据域我们用一个结构体BOX,代表位置格子,这个格子上有坐标信息和它从哪个格子过来的信息(pre),之后我们可以根据这个来输出路径。
接下来就是bfs的框架了:
我们用一个step数组来标记当前位置有没有走过防止重复遍历。并且这个数组的大小应当设为step[end+1],原因是数组默认是从0计数的,我们输入终点为18的话,若为step【end】,则越界了。
框架:
int bfs(起点,终点)
{
声明标志数组step【end+1】并初始化为全0表示都没有走过;这里可以用memset来着,忘记怎么用了。
起点的pre记为-1;
起点对应的step记为-1表示走过了.
声明一个临时BOX e用来出队入队
声明一个坐标量 “新起点”nst,用来保存下一位置的信息
声明一个方向标志量di;
初始化一个顺序队列q;
①将起点信息录入e并入队
while(q不空)
{
出队一个位置块e,
得到该位置nst=e.pos
if(位置e是终点){
输出路径
释放队列内存}
//如果不是的话
for(di=0;di<移动方式个数;di++)
{
switch(di){
case 方式1
计算nst ;break;
case 方式2
计算nst ;break;
…}
if(如果nst位置尚未走过,根据step判断)
{
将该位置入队(更新e.pos,记录前一位置到e.pre,前一位置在q->front)
这里我觉得可能思维混乱的原因是没明白进队,入队怎么操作的
进队的话是rear++,出队是front++,所以在尚未进队前,front指向的是前一个位置
标记该位置已经走过
}
}
//如果队列空的情况下还没有找到终点,说明没有路(迷宫),因为图已经遍历完成了
释放队列内存
return 0;
下面用图来分析一下BFS和顺序队列求最短路径_第1张图片这里就是算法在队列上看是怎么样的,其中起点S进入后,会将它能走的A,B,C三种方式的位置入队,
起点不是终点(这里不说起点就是终点的情况,那没有意义了)
所以将所有可走的方式入队,然后重复这个过程,S_1(A)的意义是起点S开始第一层(看成树)第一个结点
依次类推,S_1_2(A)是从S_1(A)开始的第一种走法,在第二层。
接着用树状图表示一下:
BFS和顺序队列求最短路径_第2张图片现在就清晰了,为什么bfs叫广度优先。它每到一个点,先把从这个点出发所有能引出的子节点入队,然后利用队列先进先出(先进就先检查)的特点一层层的遍历。这样能最先找到的解必然是最短路径

呼~终于描述完了,自己思路也清晰了。
有问题一定帮我指出来嗷!

你可能感兴趣的:(数据结构,大学时光,数据结构,BFS)