层层递进——宽度优先搜索(BFS)

问题引入

我们接着上次“解救小哈”的问题继续探索,不过这次是用宽度优先搜索(BFS)。

注:问题来源可以点击这里 http://www.cnblogs.com/OctoptusLian/p/7429645.html

最开始小哼在入口(1,1)处,一步之内可以到达的点有(1,2)和(2,1)。

但是小哈并不在这两个点上,那小哼只能通过(1,2)和(2,1)这两点继续往下走。

比如现在小哼走到了(1,2)这个点,之后他又能够到达哪些新的点呢?有(2,2)。再看看通过(2,1)又可以到达哪些点呢?可以到达(2,2)和(3,1)。

此时你会发现(2,2)这个点既可以从(1,2)到达,也可以从(2,1)到达,并且都只使用了两步。

注:为了防止一个点多次被走到,这里需要一个数组来记录一个点是否已经被走到过。

此时小哼2步可以走到的点就全部走到了,有(2,2)和(3,1),可是小哈并不在这两个点上。看来没有别的办法,还得继续往下尝试,看看通过(2,2)和(3,1)这两个点还能到达哪些新的没有走到过的点。

通过(2,2)这个点我们可以到达(2,3)和(3,2),通过(3,1)可以到达(3,2)和(4,1)。

现在三步可以到达的点有(2,3)、(3,2)和(4,1),依旧没有到达小哈的所在点,所以我们需要继续重复刚才的做法,直到找到小哈所在点为止。

解决步骤

回顾一下刚才的算法,可以用一个队列来模拟这个过程。在这里我们用一个结构体来实现队列

struct note{
    int x;  //横坐标
    int y;  //纵坐标
    int s;  //步数
};
struct note que[2501];  //因为地图大小不超过50*50,因此队列扩展不会超过2500个
int head,tail;
int a[51][51] = {0};  //用来存储地图
int book[51][51] = {0};  //数组book的作用是记录哪些点已经在队列中了,防止一个点被重复扩展,并全部初始化为0

/*最开始的时候需要进行队列初始化,即将队列设置为空*/
head = 1;
tail = 1;
//第一步将(1,1)加入队列,并标记(1,1)已经走过。
que[tail].x = 1;
que[tail].y = 1;
que[tail].s = 0;
tail++;
book[1][1] = 1;

然后从(1,1)开始,先尝试往右走到达了(1,2)。

tx = que[head].x;
ty = que[head].y+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;  //注意bfs每个点通常情况下只入队一次,和深搜不同,不需要将book数组还原
//插入新的点到队列中
que[tail].x = tx;
que[tail].y = ty;
que[tail].s = que[head].s+1;  //步数是父亲的步数+1
tail++; 

接下来还要继续尝试往其他方向走。

在这里我们规定一个顺序,即按照顺时针的方向来尝试(右→下→左→上)。

我们发现从(1,1)还是可以到达(2,1),因此也需要将(2,1)也加入队列,代码实现与刚才对(1,2)的操作是一样的。

对(1,1)扩展完毕后,此时我们将(1,1)出队(因为扩展完毕,已经没用了)。

head++;

接下来我们需要在刚才新扩展出的(1,2)和(2,1)这两个点上继续向下探索(因为还没有到达小哈所在的位置,所以还需要继续)。

(1,1)出队之后,现在队列的head正好指向了(1,2)这个点,现在我们需要通过这个点继续扩展,通过(1,2)可以到达(2,2),并将(2,2)也加入队列。

(1,2)这个点已经处理完毕,于是可以将(1,2)出队。(1,2)出队之后,head指向了(2,1)这个点。通过(2,1)可以到达(2,2)和(3,1),但是因为(2,2)已经在队列中,因此我们只需要将(3,1)入队。

到目前为止我们已经扩展出从起点出发2步以内可以到达的所有点,可是依旧没有到达小哈所在的位置,因此还需要继续扩展,直到找到小哈所在的点才算结束。

为了方便向四个方向扩展,这里需要一个next数组:

int next[4][2] = {  //顺时针方向
    {0,1};  //向右走
    {1,0};  //向下走
    {0,-1};  //向左走
    {-1,0};  //向上走
}

完整代码如下

#include<stdio.h>
struct note{
    int x;  //横坐标
    int y;  //纵坐标
    int f;  //父亲在队列中的编号(本题不需要输出路径,可以不需要f)
    int s;  //步数
};
int main()
{
    struct note que[2501];  //因为地图大小不超过50*50,因此队列扩展不会超过2500个

    int a[51][51] = {0};  //用来存储地图
    int book[51][51] = {0};  //数组book的作用是记录哪些点已经在队列中了,防止一个点被重复扩展,并全部初始化为0
    //定义一个用于表示走的方向的数组
    int next[4][2] = {  //顺时针方向
    {0,1}, //向右走
    {1,0},  //向下走
    {0,-1},  //向左走
    {-1,0},  //向上走
    };

    int head,tail;
    int i,j,k,n,m,startx,starty,p,q,tx,ty,flag;

    scanf("%d %d",&n,&m);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    scanf("%d %d %d %d",&startx,&starty,&p,&q);

    //队列初始化
    head = 1;
    tail = 1;
    //往队列插入迷宫入口坐标
    que[tail].x = startx;
    que[tail].y = starty;
    que[tail].f = 0;
    que[tail].s = 0;
    tail++;
    book[startx][starty] = 1;

    flag = 0;  //用来标记是否到达目标点,0表示暂时没有到达, 1表示已到达
    while(head < tail){  //当队列不为空时循环
        for(k=0;k<=3;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;  //把这个点标记为已经走过。注意bfs每个点通常情况下只入队一次,和深搜不同,不需要将book数组还原
                    //插入新的点到队列中
                    que[tail].x = tx;
                    que[tail].y = ty;
                    que[tail].f = head;  //因为这个点是从head扩展出来的,所以它的父亲是head,本题不需要求路径,因此可省略
                    que[tail].s = que[head].s+1;  //步数是父亲的步数+1
                    tail++; 

                }
            if(tx == p && ty == q)  //如果到目标点了,停止扩展,任务结束,退出循环
            {
                flag = 1;  //重要!两句不要写反
                break;
            }
        }
        if(flag == 1)
            break;
        head++;  //当一个点扩展结束后,才能对后面的点再进行扩展
    }
    printf("%d",que[tail-1].s);  //打印队列中末尾最后一个点,也就是目标点的步数
     //注意tail是指向队列队尾(最后一位)的下一个位置,所以这里需要减1
    getchar();getchar();
    return 0;
}

第一行有两个数n m,n表示迷宫的行数,m表示迷宫的列数。

接下来n行m列为迷宫,0表示空地,1表示障碍物。

最后一行四个数,前两个数表示迷宫入口的x和y坐标,后两个为小哈的x和y坐标。

写在最后

通过本次学习,我们知道一道问题的解决方法是多种多样的,不光可以用深度优先搜索来解,也可以用宽度优先搜索。

个人感觉宽度优先搜索就像是一次病原体的扩散,目的是要以最短的速度扩散到最广的范围。

注:文章内容源自《啊哈算法》

时间: 2024-10-05 01:59:03

层层递进——宽度优先搜索(BFS)的相关文章

广度/宽度优先搜索(BFS)详解

广度/宽度优先搜索(BFS) [算法入门] 1.前言 广度优先搜索(也称宽度优先搜索,缩写BFS,以下采用广度来描述)是连通图的一种遍历策略.因为它的思想是从一个顶点V0开始,辐射状地优先遍历其周围较广的区域,故得名. 一般可以用它做什么呢?一个 广度/宽度优先搜索(BFS) 算法导论里边会给出不少严格的证明,我想尽量写得通俗一点,因此采用一些直观的讲法来伪装成证明,关键的point能够帮你get到就好. 2.图的概念 刚刚说的广度优先搜索是连通图的一种遍历策略,那就有必要将图先简单解释一下.

挑战程序2.1.5 穷竭搜索&gt;&gt;宽度优先搜索(练POJ3669)

先对比一下DFS和BFS         深度优先搜索DFS                                   宽度优先搜索BFS 明显可以看出搜索顺序不同. DFS是搜索单条路径到底部,再回溯. BFS是搜索近的状态,直到底部,一般在求解最短路径或者最短步数上应用. BFS要用到队列呢.. 队列的用法看一看http://blog.csdn.net/cindywry/article/details/51919282 练习题系列--------------------- 题目:p

宽度优先搜索(BFS)

宽度优先搜索,又称为广度优先搜索,简称BFS 搜索过程:从初始结点开始,逐层向下扩展,即第n层搜索未完成,不得进入下一层搜索 一.初始结点入队,进入循环 二.取出队列的第一个元素 三.判断该结点是不是目标结点,如果是目标结点,则问题解决,跳出循环 四.如果该结点不是目标结点,判断其是否能够扩展,若不能,跳到步骤二 五.如果该结点能扩展,将其子结点放入队列的尾部 六.跳到步骤二 用一个经典的例子(走迷宫)来感受下 给定一个二维数组 int a[10][10] = {0 , 1 , 0 , 0 ,

算法7-4:宽度优先搜索

宽度优先搜索的基本思想就是先将源点添加到一个队列中, 每次从队列中取出一个顶点,将该顶点的邻居顶点中没有拜访过的顶点都添加到队列中,最后直到队列消耗完毕为止. 应用 宽度优先搜索可以解决最短路径问题.而最短路径算法在互联网中应用非常广泛,尤其是路由这块.因为路由追求的是高效,所以每个路由路径都是通过最短路径计算出来的.如果没有最短路径算法,我们可能就享受不到这么快的网速了. 另外,宽度优先搜索在快递行业也会用到,用于计算最短路径. 代码 import java.util.Stack; /** *

宽度优先搜索

Breadth First Search 宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型.Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想.其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果.换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止. 与树的层序遍历一样 遍历结果为 1 2 3 4 5 6 .....21 22 操作: 根节点入队: wh

(总结)宽度优先搜索(Breadth First Search)

ACM入门最经典的开局一般都是宽搜. 宽度优先搜索(以下均简称bfs)一般用于树和图的搜索,在ACM中属于比较基础的技巧,因此需要非常熟练的掌握. 那么从最基础的bfs开始讲起.在一个迷宫中,有一个起点和一个终点(出口),和一些障碍物(无法通过). 比如下图

2.3 基于宽度优先搜索的网页爬虫原理讲解

上一节我们下载并使用了宽度优先的爬虫,这一节我们来具体看一下这个爬虫的原理. 首先,查看HTML.py的源代码. 第一个函数: def get_html(url): try: par = urlparse(url) Default_Header = {'X-Requested-With': 'XMLHttpRequest', 'Referer': par[0] + '://' + par[1], 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64)

基础算法 --- BFS(广度优先搜索/宽度优先搜索)

个人理解 BFS是一种最简便的图搜索算法,通过遍历整张图直到找到目标节点: 从算法的角度看,所有因为展开节点而得到的字节点会被存储到一个FIFO的数据结构中,被遍历过的节点存储在一个容器中(一般是一个set),防止重复搜索 图解 如图所示,我们想要获取从S到E节点的最短路径,运用BFS,如何处理?? 主要思想是:从节点S开始将其所有临近子节点存放到一个队列中,然后标记这些节点到最初顶点S的距离为1: 然后根据队列的先进先出特定,出队列,判断是否为目标节点:如果不是,将出队节点的临近子节点入队,直

&quot;《算法导论》之‘图’&quot;:深度优先搜索、宽度优先搜索及连通分量

本文兼参考自<算法导论>及<算法>. 以前一直不能够理解深度优先搜索和广度优先搜索,总是很怕去碰它们,但经过阅读上边提到的两本书,豁然开朗,马上就能理解得更进一步.  1. 深度优先搜索  1.1 迷宫搜索 在<算法>这本书中,作者写了很好的一个故事.这个故事让我马上理解了深度优先搜索的思想. 如下图1-1所示,如何在这个迷宫中找到出路呢?方法见图1-2. 图1-1 等价的迷宫模型 探索迷宫而不迷路的一种古老办法(至少可以追溯到忒修斯和米诺陶的传说)叫做Tremaux搜