• “生动”讲解——深度优先搜索与广度优先搜索


    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持!


    深度优先搜索(Depth First Search,DFS)

    主要思想:不撞南墙不回头

    深度优先遍历的主要思想就是:首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点;当没有未访问过的顶点时,则回到上一个顶点,继续试探访问别的顶点,直到所有的顶点都被访问。

    沿着某条路径遍历直到末端,然后回溯,再沿着另一条进行同样的遍历,直到所有的顶点都被访问过为止。

    图解:

    这里写图片描述

    分析:

    通过上面的图例可以非常直观的了解深度优先搜索的工作方式。下面来分析一下如何用代码来实现它。
    大家都知道,深度优先的主要思想就是“不撞南墙不回头”,“一条路走到黑”,如果遇到“墙”或者“无路可走”时再去走下一条路。

    所以先规定好一个走路的规则,比如就按照右下左上顺时针的方式去尝试。

    如上图僵尸的位置是起始点,先往右走,但是有堵墙走不了,所以按照规定尝试往下走。到达“1”的位置,此时重复刚才的步骤,先向右走可以走到“2”,再重复规则发现向右可以走到“3”,再重复发现“下右左上”四个方向都不能走,这时候就退回“2”的位置尝试向下走。。。依次类推直到走到最终的目的地。

    聪明的你肯定已经发现了“递归”是实现深度优先搜索的最好的方式。定义好规则然后就这样递归的循环下去直到走到终点,请看下面的伪代码:

    void dfs()  
    {  
        // 判断是否到达终点
        if() {
            return;
        }
    
        // 尝试每一个可走方向(右下左上) 
        for(i=0; i<n; i++){ 
            // 判断是否可走,可走调用递归尝试下一步,不可走则尝试该点的其他方向
            if () {
                // 继续下一步  
                dfs();  
            }
        }  
    }  

    代码:

    程序中完整的深度优先搜索代码如下:

        // 深度优先搜索
        public void DFS( int x, int y )
            throws Exception
        {
    
            int tx, ty;
    
            int[] pos =
                { x, y };
            dfs_posList.add(pos);
    
            // 是否到达目的地
            if (mMapView[y][x] == 8)
            {
                throw new Exception("find");
            }
    
            // 顺时针循环,右下左上四个方向
            for (int k = 0; k < 4; k++)
            {
                tx = x + next[k][1];
                ty = y + next[k][0];
    
                // 是否出了边界
                boolean isOut = tx < 0 || tx >= mapWidth || ty < 0 || ty >= mapHeight;
                if (!isOut)
                {
    
                    // 是否是障碍物
                    if (mMapView[ty][tx] == 0 && dfs_book[tx][ty] == 0 || mMapView[ty][tx] == 8)
                    {
                        dfs_book[tx][ty] = 1;
                        DFS(tx, ty);
                        dfs_book[tx][ty] = 0;
                    }
                }
    
            }
        }
    
    // 判断方向的数组
    int[][] next =
            {
                    { 0, 1 }, // 右
                    { 1, 0 }, // 下
                    { 0, -1 }, // 左
                    { -1, 0 } // 上
            };

    广度优先搜索(Breadth First Search, BFS)

    主要思想:层层递进

    首先以一个未被访问过的顶点作为起始顶点,访问其所有相邻的顶点,然后对每个相邻的顶点再访问它们相邻的未被访问过的顶点,直到所有顶点都被访问过,遍历结束。

    图解:

    这里写图片描述

    分析:

    通过两个图例的对比,可以清楚的看到两种搜索算法的区别。
    深度优先就是一条路走到底,然后再尝试下一条路。
    广度优先就是走到一个点,将该点周围可走的点走完,然后再按同样规则走其他的点。(大家可以想像一下铺满的多米诺骨牌,将其中一块推倒其余周围的骨牌也会一层层扩散式的倒塌)

    所以先规定好一个走路的规则,同样按照右下左上顺时针的方式去尝试。

    如上图僵尸的位置是起始点,先往右走,但是有堵墙走不了,所以按照规定尝试往下走。到达“1”的位置,在“1”处可以知道四周能走的点只有“2”。“2”能走的点有“3”,“4”。来到“3”,发现没有可走的。来到“4”发现去可以去“5”。这样依次进行下去就完成了广度优先搜索。

    我们可以通过“队列”来模拟上面的过程,伪代码如下:

    void dfs()  
    {  
        // 创建一个队列
        Queue queue = new Quene();
        // 将起点加入队列中
        queue.offer("起点");
    
        // 循环队列直到队列没有元素
        while(queue!null) {
            // 从队列中取出一个点(并移除)
            p = quene.poll();
    
            // 循环尝试p点附近可走的点(右下左上) 
            for(i=0; i<n; i++) {
    
                // 判断是否到达目的地
                if() {
                }
    
                // 判断是否可走,可走将该点加入到队列中,不可走则尝试该点的其他方向
                if () {
                    queue.offer("新点");
                }
            }  
        } 
    
    }  

    完整代码:

    // 广度优先搜索(Breadth First Search)
        public void BFS()
        {
    
            // 存储点的序列
            Queue<int[]> queue = new LinkedList<int[]>();
    
            int x, y;
            int[] pos =
                { 0, 0 };
            queue.offer(pos);
    
            while (!queue.isEmpty())
            {
                // 从队列中取出并移除
                pos = queue.poll();
                bfs_posList.add(pos);
    
                // 顺时针循环,右下左上四个方向
                for (int k = 0; k < 4; k++)
                {
                    x = pos[0];
                    y = pos[1];
    
                    // 是否到达目的地
                    if (mMapView[y][x] == 8)
                    {
                        return;
                    }
    
                    x += next[k][1];
                    y += next[k][0];
    
                    // 是否出了边界
                    boolean isOut = x < 0 || x >= mapWidth || y < 0 || y >= mapHeight;
                    if (!isOut)
                    {
                        // 是否是障碍物
                        if (mMapView[y][x] == 0 && bfs_book[x][y] == 0  || mMapView[y][x] == 8)
                        {
                            bfs_book[x][y] = 1;
                            queue.offer(new int[]
                                { x, y });
                        }
                    }
    
                }
            }
        }
    
        int[][] next =
            {
                    { 0, 1 }, // 右
                    { 1, 0 }, // 下
                    { 0, -1 }, // 左
                    { -1, 0 } // 上
            };
    

    写在最后

    相信通过上面的配图和讲解可以很清晰的理解深度优先搜索和广度优先搜索,如果今后有时间我争取再做一个最短路径的小Demo。

    本例代码已经上传到我的github,感兴趣的可以下载来玩玩(纯属娱乐没啥实际价值):https://github.com/a396901990/PathFind

  • 相关阅读:
    C语言内存调试技巧—C语言最大难点揭秘
    include .h 以及.cpp的记录
    VS2010中<无法打开包括文件:“iostream.h”:>错误解决方法
    #include<iostream>与#include<iostream.h>的区别
    VS2013/2012 下无法打开 源 文件“stdafx.h”的解决方法
    【Oracle】Oracle日期格式详解
    【EasyUI】 日期格式化
    【TortoiseGit】TortoiseGit将本地库push到远端
    【Tomcat】解决Eclipse无法添加Tomcat Service问题
    【Tomcat】配置Tomcat
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6168025.html
Copyright © 2020-2023  润新知