• 【算法】深度搜索(DFS) 和 广度搜索(BFS)


    深度搜索(DFS)

    点;然后退回到该顶点,搜索其它路径,直到以该顶点为始点的所有路径的顶点都被访问,深度搜索算法是递归算法,因为对于没一个节点来说,执行的是同样的操作。
     简单来说,深度搜素算法就是一条道走到黑,走不动了再回溯回去,选择其他路径,举个例子,对于下面的图,假设从1开始遍历:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    用途

    深度搜索常用于解决图的遍历问题(尤其是矩阵图如迷宫问题等),比如求解从某一个点到另一个点的最短距离,则只需遍历所有路径,在遍历同时记录路径长度,最后一定能找到最短的距离,但这种方法复杂度较高,因为要遍历完所有结点才能找到。
    深度搜索是回溯法的主要方法,沿着一条路一直走,走不通再回溯到上一节点,选择其他路径。

    深度搜索模板

    int check(参数)
    {
        if(满足条件)
            return 1;
        return 0;
    }
     
    void dfs(int step)
    {
        判断边界if
        {
            到达边界时的操作(输出等)
        }
        未到边界时尝试每一种可能else
        {
            满足check条件 if
            {
                标记
                继续下一步 : dfs(step+1)
                恢复初始状态
            }
        };
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    深度搜索经典例题:【n-皇后问题】

    n−皇后问题是指将 n 个皇后放在 n×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
    在这里插入图片描述
    现在给定整数 n,请你输出所有的满足条件的棋子摆法。
    输入格式
    共一行,包含整数 n。
    输出格式
    每个解决方案占 n 行,每行输出一个长度为 n 的字符串,用来表示完整的棋盘状态。其中 . 表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。每个方案输出完成后,输出一个空行。注意:行末不能有多余空格。输出方案的顺序任意,只要不重复且没有遗漏即可。
    数据范围
    1≤n≤9
    输入样例
    4
    输出样例
    .Q…
    …Q
    Q…
    …Q.

    …Q.
    Q…
    …Q
    .Q…

    (DFS按行枚举) 时间复杂度 O ( n ! ) O(n!)O(n!)
    代码分析

    对角线 d g [ u + i ] dg[u+i]dg[u+i],反对角线u d g [ n − u + i ]
    udg[n−u+i]udg[n−u+i]中的下标 u + i u+iu+i和 n − u + i n−u+in−u+i 表示的是截距

    下面分析中的 ( x , y ) (x,y)(x,y) 相当于上面的 ( u , i ) (u,i)(u,i)
    反对角线 y = x + b y=x+by=x+b, 截距 b = y − x b=y−xb=y−x,因为我们要把 b 当做数组下标来用,显然 b 不能是负的,所以我们加上 +n(实际上+n+4,+2n都行),来保证是结果是正的,即 y − x + n y - x + ny−x+n
    而对角线 y = − x + b y=−x+by=−x+b, 截距是 b = y + x b=y+xb=y+x,这里截距一定是正的,所以不需要加偏移量
    核心目的:找一些合法的下标来表示 d g dgdg 或 u d g udgudg 是否被标记过,所以如果你愿意,你取 u d g [ n + n − u + i ] udg[n+n−u+i]udg[n+n−u+i] 也可以,只要所有 ( u , i ) (u,i)(u,i) 对可以映射过去就行
    题目答案

    #include
    
    using namespace std;
    const int N = 100;
    char g[N][N];  // g[N][N]用来存路径
    // bool数组用来判断搜索的下一个位置是否可行
    bool col[N],dg[N],udg[N]; // g[N][N]用来存路径
    int n;
    
    void dfs(int x){
        // u == n 表示已经搜了n行,故输出这条路径
        if(x == n){
            for (int i = 0; i < n; ++i) puts(g[i]); // 等价于cout << g[i] << endl;
            puts(""); // 换行
            return;
        }else{
            for (int i = 0; i < n; ++i) {
                // 剪枝(对于不满足要求的点,不再继续往下搜索)
                // udg[n - u + i],+n是为了保证下标非负
                if(!col[i] && !dg[x+i] && !udg[n-x+i])
                {
                    g[x][i] = 'Q';
                    col[i] = dg[x+i] = udg[n-x+i] = true;
                    dfs(x+1);
                    g[x][i] = '.';
                    col[i] = dg[x+i] = udg[n-x+i] = false;// 恢复现场 这步很关键
                }
    
            }
        }
    
    }
    int main() {
        cin>>n;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                g[i][j] = '.';
            }
        }
        dfs(0);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    广度搜索(BFS)

    搜索方法

    广度搜索,顾名思义,就是更大范围内搜索,与深度搜索不同的是,深度搜索是一次搜索一条路径,一直搜索到走不通为止;而广度搜索是同时搜索所有路径,相当于一层一层地搜索,就好比波浪的扩展一样。此搜索方法跟树的层次遍历类似,因此宽度搜索一般都用队列存储结构。搜索原则:

    搜索方法

    (1)访问遍历出发顶点,该顶点入队
    (2)队列不空,则队头顶点出队;
    (3)访问出队顶点所有的未访问邻接点并将其入队;
    (4)重复(2)(3),直到队空或者找到目标点
    举个例子,还是对下面这个图进行广度遍历:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    用途

    虽然看似广度搜索一次扩张了很多个点,但实际上任然是一个结点一个结点地搜索,只是它是以层层扩张的方式进行搜索。广度搜索也常用于解决图的遍历,在求一个点到另一个点的最短距离时,广度搜索比深度搜索更有优势,因为广度搜索是层层遍历的,所以一定存在某条路径最先到达目标点,只要找到目标点后就退出,就不用搜索所有点。
     广度搜索也是分支限界法的主要算法(当然,分支限界也可能是广度搜索和宽度搜索的结合)。广度搜索最常解决的问题类型是:求从某一个状态到另一个状态的最小步数,每一个状态对应多个(扩展结点个数)不同的操作。

    广度搜索模板

    #include
    #include
    using namespace std;
    struct state
    {
    	int a,b;//存储相应信息
    	int step;//存储当前结点的步数
    };
    queueQ;
    int bfs(int a,int b,int A,int B)//返回最小步数
    {
    	//参数a,b是初始状态,
    	//参数A,B是目标状态判断条件;
    	while(!Q.empty())Q.pop();//清空队列
    	state P;
    	P.a=a;P.b=b;P.step=0;//初始结点
    	Q.push(P);//初始结点入队
    	while(!Q.empty())//队列非空
    	{
    		state head=Q.front();//保存队头
    		Q.pop();//队头出队
    		//以下扩展每个结点的邻接结点*************************
    		state p=head;
    		p.a=...//执行操作
    		p.b=...//执行操作
    		p.step++;//步数加一
    		//判断p.a,p.b是否合法(剪枝处理)
    		if(ok(p.a,p.b))
    		{
    			if(p.a==A&&p.b==B)//判断该状态是否满足目标状态
    			return p.step;//返回步数
    			Q.push(p);//否则入队
    		}
    		
    		//扩展其他结点
    		......
    		//扩展结点结束*************************************
    	}
    	return -1;//不能到达目标状态,返回-1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    深度搜索经典例题:【走迷宫——边的权值相同】

    给定一个 n ∗ m n*mn∗m 的二维整数数组,用来表示一个迷宫,数组中只包含 0 或 1,其中 0 表示可以走的路,1 表示不可通过的墙壁。
    最初,有一个人位于左上角 ( 1 , 1 ) (1, 1)(1,1)处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。
    请问,该人从左上角移动至右下角 ( n , m ) (n, m)(n,m) 处,至少需要移动多少次。
    据保证 ( 1 , 1 ) (1, 1)(1,1) 处和 ( n , m ) (n, m)(n,m) 处的数字为 0,且一定至少存在一条通路。
    输入格式
    第一行包含两个整数n和m。
    接下来n行,每行包含m个整数(0或1),表示完整的二维数组迷宫。
    输出格式
    输出一个整数,表示从左上角移动至右下角的最少移动次数。
    数据范围
    1≤n,m≤100
    输入样例
    5 5
    0 1 0 0 0
    0 1 0 1 0
    0 0 0 0 0
    0 1 1 1 0
    0 0 0 1 0
    输出样例
    8
    答案解析

    #include
    
    using namespace std;
    const int N = 110;
    int n,m;
    int g[N][N],d[N][N];  // g记录迷宫,d记录该位置与起始位置的距离
    typedef pair PII;
    queue q;
    
    int bfs(){
        int dx[] = {-1,0,1,0},dy[] = {0,1,0,-1};
    
        q.push({0,0}); //从第一个元素开始访问
        d[0][0] = 0;
    
        while(!q.empty()){
            auto t = q.front();  q.pop();
            
            for(int i = 0;i < 4;i++){
                int x = t.first+dx[i] , y = t.second+dy[i];
                
                if(x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 0 && !d[x][y]){
                    d[x][y] = d[t.first][t.second]+1;
                    q.push({x,y});
                }
            }
        }
        return d[n-1][m-1];
    }
    
    int main() {
        cin>>n>>m;
        for (int i = 0; i < n; ++i) 
            for (int j = 0; j < m; ++j) 
                cin>>g[i][j];
    
        cout<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    来源

    深度搜索(DFS)和广度搜索(BFS)——1
    深度搜索(DFS)和广度搜索(BFS)——2

  • 相关阅读:
    nvm 常用命令
    【PAT甲级】1061 Dating
    《工程伦理与学术道德》之《工程中的风险、安全与责任》
    jvm调优-内存泄漏导致cpu飙升
    CesiumJS【Basic】- #024 加载webm文件(Primitive方式)
    多模态GPT-V出世!36种场景分析ChatGPT Vision能力,LMM将全面替代大语言模型? | 京东云技术团队
    APP备案流程详细解读
    在 Vue & react 中,哪些地方用到闭包?
    【C++】类和对象(下)
    WFP实现侧边栏导航菜单
  • 原文地址:https://blog.csdn.net/weixin_44231544/article/details/126048113