BFS/DFS 广度/深度优先搜索


深度优先搜索DFS

所谓深度优先搜索,通俗点理解就是一条路走到头--不撞南墙不回头。

我们先来看一个全排列问题,现在要对1 2 3进行全排列,现在小哼手上拿着1 2 3三张卡片,他要将这三张卡片放入三个盒子里,每放满不就是一种全排列了么?


但是每次到底是先放卡片1还是卡片2,3呢?

小哼想,我按顺序放吧,每次都按照1.2.3的顺序放卡片。于是他走到1号盒子前把卡片1放入,走到2号盒子前把卡片2放入,走到3号盒子前把卡片3放入,走到四号盒子...但小哼的卡片已经放完

啦。这时就产生了一种全排列“1 2 3”啦。

但是还有好多种情况啊,于是产生了一种全排列后小哼要立即返回,他回到3号盒子前,将卡片3取回,看还能不能放其他卡片,显现小哼现在手里没除了卡片3外没有其他卡片了,所以小哼继续

往后退,到了2号盒子前,小哼将卡片2收回,这是小哼手里已经有两张卡片了,于是他将卡片3放入2号盒子,放好后又往后走一步来到三号盒子,将卡片2放入,又来到四号盒子前,当然没有

四号盒子啦,于是又产生了一种全排列“1 3 2

按照这样模拟后,便会依次生成全排列 “2 1 3” “2 3 1” “3 1 2” “3 2 1”

说了半天,我们看下如何用代码实现吧

先将i号卡片放入第step个盒子中

for(i = 1; i <=n; i++)
    {
        a[step] = i;  //将卡片i放入第step个盒子里
    }

但这里还有个问题,如何一张卡片已经放入其他盒子中了,就不能再放入当前的盒子中。所以,我们需要一个book[]数组来标记手上还有没有这张卡片

for(i = 1; i <= n; i++)
    {
        if(book[i] == 0) / /如果手上有这张卡片
        {
            a[step] = i; //将卡片i放入第step个盒子里
            book[i] = 1; //此时手上已经没有这张卡了 标记一下
        }
   }

处理完第step个盒子后我们要向后走一步,处理第step+1个盒子,显然处理第step+1个盒子的方法和第step个盒子的一样,那么我们将刚才处理第step个盒子的方法写成一个函数

void dfs(int step)
{
    for(i = 1; i <= n; i++)
        {
            if(book[i] == 0) / /如果手上有这张卡片
            {
                a[step] = i; //将卡片i放入第step个盒子里
                book[i] = 1; //此时手上已经没有这张卡了 标记一下
            }
       }
}

处理第step+1个盒子只有dfs(step+1)就行啦

void dfs(int step)
{
    for(i = 1; i <= n; i++)
        {
            if(book[i] == 0) / /如果手上有这张卡片
            {
                a[step] = i; //将卡片i放入第step个盒子里
                book[i] = 1; //此时手上已经没有这张卡了 标记一下
            }
            dfs(step+1);//处理下个盒子
            book[i] = 0; //这步很重要!!往回走时要将之前的卡片收回!
       }
}

book[i] = 0;是将之前的卡片收回,如果不把刚才放入盒子的卡片收回,那么就无法再进行下一次摆放。
还剩最后一个问题,什么时候输出一个满足条件的序列呢?其实当我们走到第n+1个盒子前时,说明前n个盒子都已经放好卡片了,这时候我们就要return返回了


void dfs(int step)
{
    if (step == n + 1) //判断边界
    {
        for (i = 1; i <= n; i++)
            cout << a[i] << " ";
        cout << endl;
        return;//返回上一个盒子前
    }
    else
    {
        for (i = 1; i <= n; i++)
        {
            if (book[i] == 0) / /如果手上有这张卡片
            {
                a[step] = i; //将卡片i放入第step个盒子里
                book[i] = 1; //此时手上已经没有这张卡了 标记一下
            }
            dfs(step + 1);//处理下个盒子
            book[i] = 0; //这步很重要!!往回走时要将之前的卡片收回!
        }
    }
}

讲到这里想必大家已经对深度优先搜索有一定了解了吧,深搜使用就是递归和回溯的思想。

广度优先搜索BFS

讲完了dfs,我们再来看看什么是广度优先搜索。

所谓广度优先搜索就是一种层层递进的搜索。

我们来看一张图。这是一个迷宫,我们用一个二维数据储存它。

现在小哼站在(1 1)处,它可以向下或向右走,如何才能到达终点呢?已经学会深搜的你因该很快就想到办法了,但这里我们用另一种方法BFS,“一层层”扩展找到终点。扩展时每发现一个点就将这个点放入队列中,知道走到终点位置。和深搜一样,我们也需要一个book数组记录是否走过这个点。

我们每对一个点扩展完毕,这个点就没用了,所以要将这个点出队,对下个点扩展。

理解了这点后代码就很容易实现了^_^。

完整代码

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int x, y;
    int s;//步数
    int f;//父亲再队列中的编号
};

int main()
{
    queue<node> que;
    int a[51][51] = { 0 };
    int book[51][51] = { 0 };
    int next[4][2] =
    {
        {0,1}, //向右走
        {1,0},//向下走
        {0,-1},//向左走
        {-1,0}//向上走
    };
    int n, m, i, j, k, flag,p,q;
    node start, tnode;
    //输入迷宫的行列 起始点
    cin >> n >> m >> start.x >> start.y;
    //输入迷宫图
    for (i = 1; i <= n; i++)
        for (j = 1; j <= m; j++)
            cin >> a[i][j];
    cin >> p >> q;//输入终点
    book[start.x][start.y] = 1;//将起始点标记为走过
    //如果队列不为空
    while (!que.empty())
    {
        //枚举四个方向
        for (int k = 0; k <= 3; k++)
        {
            //计算下一步坐标
            tnode.x = que.front().x + next[k][0];
            tnode.y = que.front().y + next[k][1];
            //判断是否出界
            if (tnode.x<1 || tnode.x>n || tnode.y<1 || tnode.y>m)
                continue;
            //判断是否是陆地或已经走过
            if (mapp[tnode.x][tnode.y] > 0 && book[tnode.x][tnode.y] == 0)
            {
                sum++;
                //每个点只入队一次,标记走过
                book[tnode.x][tnode.y] = 1;
                int ts = que.back().s;//记录父亲的步数
                //将该点入队
                que.push(tnode);
                que.back().s = ts+1; //步数是父亲步数加1
            }
            if (tnode.x == p && tnode.y == q)
            {
                flag = 1;
                break;
            }
        }
        if (flag == 1)
            break;
        que.pop();//队首出队
    }

    cout << que.back().s << endl;
    system("pause");
    return 0;
}

练习

学完DFS和BFS后来练习一下吧。

题目:宝岛探索

小哼通过一种秘密方法得到一张不完整的钓鱼岛航拍图。小哼决定去钓鱼岛探险。

下面的10*10的矩阵就是该航拍图。图中数字表示海拔,0表示海洋,1-9都表示陆地。

小哼的飞机会降落再(6,8),现在需要计算小哼降落岛的面积。此处我们把降落点上

下左右相邻岛视为同一岛屿。

输入

输出

BFS代码如下:

#include<bits/stdc++.h>
using namespace std;

struct node
{
    int x;
    int y;
};
int main()
{
    queue<node> que;
    int mapp[51][51];
    int book[51][51];
    int i, j, n, m,sum;
    node start, tnode;
    memset(book, 0, sizeof(book));
    memset(mapp, 0, sizeof(mapp));
    //输入地图的行列,初始坐标
    cin >> n >> m >> start.x >> start.y;
    //输入地图
    for(i=1;i<=n;i++)
        for (j = 1; j <= m; j++)
        {
            cin >> mapp[i][j];
        }
    int next[4][2]=
    {
        {0,1}, //向右走
        {1,0},//向下走
        {0,-1},//向左走
        {-1,0}//向上走
    };
    //向队列插入降落的起始坐标
    que.push(start);
    sum = 1;
    book[start.x][start.y] = 1;
    //当队列不为空时循环
    while (!que.empty())
    {
        //枚举四个方向
        for (int k = 0; k <= 3; k++)
        {
            //计算下一步坐标
            tnode.x = que.front().x + next[k][0];
            tnode.y= que.front().y + next[k][1];
            //判断是否出界
            if (tnode.x<1 || tnode.x>n || tnode.y<1 || tnode.y>m)
                continue;
            //判断是否是陆地或已经走过
            if (mapp[tnode.x][tnode.y] > 0 && book[tnode.x][tnode.y] == 0)
            {
                sum++;
                //每个点只入队一次,标记走过
                book[tnode.x][tnode.y] = 1;
                //将该点入队
                que.push(tnode);
            }

        }
        //队首出队,下个点继续搜素
        que.pop();
    }
    cout << sum << endl;
    getchar();
    getchar();
    return 0;
}

DFS代码如下:

#include<bits/stdc++.h>
using namespace std;

struct node
{
    int x;
    int y;
};
int mapp[51][51];
int book[51][51];
int n, m,sum;
void dfs(int x,int y)
{
    int next[4][2] =
    {
        {0,1}, //向右走
        {1,0},//向下走
        {0,-1},//向左走
        {-1,0}//向上走
    };
    int tx, ty, k;
    //枚举四个方向
    for (k = 0; k <= 3; k++)
    {
        tx = x + next[k][0];
        ty = y + next[k][1];
        //判断是否出界
        if (tx<1 || tx>n || ty<1|| ty>m)
            continue;
        //判断是否是陆地或已经走过
        if (mapp[tx][ty] > 0 && book[tx][ty] == 0)
        {
            sum++;
            //标记走过
            book[tx][ty] = 1;
            dfs(tx, ty); //执行下一步
        }
    }
    return;
}
int main()
{

    int i, j,sx,sy;
    memset(book, 0, sizeof(book));
    memset(mapp, 0, sizeof(mapp));
    cin >> n >> m >> sx >> sy;
    //输入地图
    for(i=1;i<=n;i++)
        for (j = 1; j <= m; j++)
        {
            cin >> mapp[i][j];
        }
    book[sx][sy] = 1;
    sum = 1;
    dfs(sx, sy);
    cout << sum << endl;
    getchar();
    getchar();
    return 0;
}

拓展:如何求有多少个独立岛屿呢?

即求独立子图的个数,这就是Floodfill漫水填充法,又叫种子填充法。Windows下“画图”中的油漆桶工具就是基于这个算法的。
我们将独立岛屿进行染色,所以增加参数color。
代码如下

#include<bits/stdc++.h>
using namespace std;

struct node
{
    int x;
    int y;
};
int mapp[51][51];
int book[51][51];
int n, m,sum;
void dfs(int x,int y,int color)
{
    mapp[x][y] = color;//表示小哼来过这个岛
    int next[4][2] =
    {
        {0,1}, //向右走
        {1,0},//向下走
        {0,-1},//向左走
        {-1,0}//向上走
    };
    int tx, ty, k;
    //枚举四个方向
    for (k = 0; k <= 3; k++)
    {
        tx = x + next[k][0];
        ty = y + next[k][1];
        //判断是否出界
        if (tx<1 || tx>n || ty<1|| ty>m)
            continue;
        //判断是否是陆地或已经走过
        if (mapp[tx][ty] > 0 && book[tx][ty] == 0)
        {
            sum++;
            //标记走过
            book[tx][ty] = 1;
            dfs(tx, ty,color); //执行下一步
        }
    }
    return;
}
int main()
{

    int i, j,sx,sy,color=0;
    memset(book, 0, sizeof(book));
    memset(mapp, 0, sizeof(mapp));
    cin >> n >> m ;
    //输入地图
    for(i=1;i<=n;i++)
        for (j = 1; j <= m; j++)
        {
            cin >> mapp[i][j];
        }
    //对每个大于0(未被染色)的点尝试dfs染色
    for (i = 1; i <= n; i++)
    {
        for (j = 1; j <= m; j++)
        {
            if (mapp[i][j] > 0)//如果没被染色
            {
                color--;//color减一表示一种新的颜色
                book[i][j] = 1; //标记走过
                dfs(i, j, color);
            }
        }

    }
    for (i = 1; i <= n; i++)
    {
        for (j = 1; j <= m; j++)
        {
            cout<<mapp[i][j]<<" ";
        }
        cout << endl;
    }
    cout <<"共有独立岛屿:"<< -color<<" 个" << endl;
    getchar();
    getchar();
    return 0;
}

原文地址:https://www.cnblogs.com/muyefeiwu/p/11315482.html

时间: 2024-10-12 02:49:31

BFS/DFS 广度/深度优先搜索的相关文章

DFS(深度优先搜索)模板

void dfs()//参数用来表示状态{    if(到达终点状态)    {        ...//根据题意来添加        return;    }    if(越界或者是不符合法状态)        return;    for(扩展方式)    {        if(扩展方式所达到状态合法)        {            ....//根据题意来添加            标记:            dfs():            修改(剪枝):         

DFS(深度优先搜索)

连通个数问题:https://nanti.jisuanke.com/t/43374 原文地址:https://www.cnblogs.com/Cnxz/p/12384823.html

算法导论——DFS深度优先搜索

package org.loda.graph; import org.loda.structure.Stack; /** * * @ClassName: DFS * @Description: 深度优先搜索(无向图) * @author minjun * @date 2015年5月24日 上午4:02:24 * */ public class DFS { //原点 private int s; // visited[i]表示i节点是否被访问过 private boolean[] visited;

[LeetCode OJ] Word Search 深度优先搜索DFS

Given a 2D board and a word, find if the word exists in the grid. The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be us

【图论】广度优先搜索和深度优先搜索

写在最前面的 这篇文章并没有非常详细的算法证明过程.导论里面有非常详细的证明过程.本文只阐述“广度优先和深度优先搜索的思路以及一些简单应用”. 两种图的遍历算法在其他图的算法当中都有应用,并且是基本的图论算法. 广度优先搜索 广度优先搜索(BFS),可以被形象的描述为“浅尝辄止”,具体一点就是每个顶点只访问它的邻接节点(如果它的邻接节点没有被访问)并且记录这个邻接节点,当访问完它的邻接节点之后就结束这个顶点的访问. 广度优先用到了“先进先出”队列,通过这个队列来存储第一次发现的节点,以便下一次的

Java用邻接矩阵实现图并进行深度优先搜索

先定义节点类 class Vertex{ char label; boolean wasVisited; public Vertex(char label){ this.label = label; wasVisited = false; } } 图: class Graph{ private final int MAX_VERTS = 20; private Vertex vertexList[];//节点列表 private int adjMat[][];//邻接矩阵 private int

算法学习——DFS(暴力搜索)N皇后问题

N皇后问题是非常经典的一道问题,解题的方法也有很多,非常经典包括暴力回溯法. DFS就是深度优先搜索的首字母,简单理解就是把所有可能是答案的结果都尝试一遍,用走迷宫来举例子的话就是一条路走到黑,如果走到死路了,再退回上一个分岔口选择另一条路继续一条路走到黑. 属于入门时非常常用的暴力算法,考察的知识点主要是对递归的掌握和理解.递归也是新生入门算法时必经的一道门槛,理解透彻递归,就能明白DFS. #include<bits/stdc++.h> using namespace std; const

深度优先搜索DFS和广度优先搜索BFS

DFS简介 深度优先搜索,从起点开始按照某个原则一直往深处走,直到找到解,或者走不下去,走不下去则回溯到前一节点选择另一条路径走,直到找到解为止. BFS简介 广度优先搜索,从起点开始先搜索其相邻的节点,由此向外不断扩散,直到找到解为止. 举例解释 从1开始去寻找5 DFS: 原则:优先选择左手边 过程:1-2-3-4-6-4-5 BFS: 队列情况:1 2.5     5.3 5出来则找到 遍历图中所有点 DFS: 原则:优先选择左手边 过程:1-2-3-4-6-4-5 BFS: 队列情况:1

深度优先搜索(DFS)与广度优先搜索(BFS)的Java实现

1.基础部分 在图中实现最基本的操作之一就是搜索从一个指定顶点可以到达哪些顶点,比如从武汉出发的高铁可以到达哪些城市,一些城市可以直达,一些城市不能直达.现在有一份全国高铁模拟图,要从某个城市(顶点)开始,沿着铁轨(边)移动到其他城市(顶点),有两种方法可以用来搜索图:深度优先搜索(DFS)和广度优先搜索(BFS).它们最终都会到达所有连通的顶点,深度优先搜索通过栈来实现,而广度优先搜索通过队列来实现,不同的实现机制导致不同的搜索方式. 1.1 深度优先搜索 深度优先搜索算法有如下规则: 规则1