• LeetCode刷题之BFS和DFS


    广度优先搜索(BFS)

    问题的本质就是让你在一幅「图」中找到从起点start到终点target的最近距离,这个例子听起来很枯燥,但是 BFS 算法问题其实都是在干这个事儿。
    把枯燥的本质搞清楚了,再去欣赏各种问题的包装才能胸有成竹嘛。
    这个广义的描述可以有各种变体,比如走迷宫,有的格子是围墙不能走,从起点到终点的最短距离是多少?如果这个迷宫带「传送门」可以瞬间传送呢?
    再比如说两个单词,要求你通过某些替换,把其中一个变成另一个,每次只能替换一个字符,最少要替换几次?
    再比如说连连看游戏,两个方块消除的条件不仅仅是图案相同,还得保证两个方块之间的最短连线不能多于两个拐点。你玩连连看,点击两个坐标,游戏是如何判断它俩的最短连线有几个拐点的?
    再比如……

    净整些花里胡哨的,这些问题都没啥奇技淫巧,本质上就是一幅「图」,让你从一个起点,走到终点,问最短路径。这就是 BFS 的本质,框架搞清楚了直接默写就好。

    ①树的层序遍历:使用队列保存未被检测的结点,结点按照宽度优先的次序被访问和进出队。
    ②有向无环图的最短路径查找:由于有向无环图的某个节点的next节点可能会与另一个节点的next节点重复,所以我们需要记录已访问过的节点

    //根节点与目标节点之间的最短路径长度
    int BFS(Node root, Node target) {
        Queue<Node> queue;  // 用来存放节点的队列
        Set<Node> used;     // 用来存放已经使用过的节点,防止走回头路。但是像一般的二叉树结构,没有子节点到父节点的指针,不会走回头路就不需要
        int step = 0;       // 步长
        
        //1:初始化
        add root to queue;
        add root to used;
        
        //2:开始进行BFS
        while (queue is not empty) {
            step = step + 1;
            
            //队列中已经存放的节点个数
            int size = queue.size();
            for (int i = 0; i < size; ++i) {
                Node cur = the first node in queue;
                return step if cur is target;
                for (Node next : the neighbors of cur) {
                    if (next is not in used) {
                        add next to queue;
                        add next to used;
                    }
                }
                remove the first node from queue;
            }
        }
        return -1;          // there is no path from root to target
    }
    

    深度优先搜索(DFS)

    一直往深处走,直到找到解或者走不下去为止,主要用于树的遍历(前序遍历,中序遍历,后序遍历)或者图的搜索问题或者回溯算法问题。
    ①树的遍历:一直向叶子节点遍历,直至遇到叶子节点表示此根节点到这个叶子节点的路径已经访问完,需要访问根节点到下一个叶子节点的路径。(当然遍历顺序主要取决于遍历的方式,比如前序遍历(根左右)、后序遍历(左根右)、后序遍历(左右根))

    ②图的搜索问题:上面我们使用了BFS来解决图的最短路径问题,同时我们也可以使用DFS来实现图的最短路径寻找。在这里我们分别提供递归版模板和用stack的迭代版模板:
    递归

    boolean DFS(Node cur, Node target, Set<Node> visited) {
        return true if cur is target;
        for (next : each neighbor of cur) {
            if (next is not in visited) {
                add next to visted;
                return true if DFS(next, target, visited) == true;
            }
        }
        return false;
    }
    
    

    非递归

    boolean DFS(int root, int target) {
        Set<Node> visited;
        Stack<Node> s;
        add root to s;
        while (s is not empty) {
            Node cur = the top element in s;
            return true if cur is target;
            for (Node next : the neighbors of cur) {
                if (next is not in visited) {
                    add next to s;
                    add next to visited;
                }
            }
            remove cur from s;
        }
        return false;
    }
    
    

    回溯法:回溯法使用DFS的策略来寻找所有可行解或者最优解,也就是回溯法的所有解可以构成一个解空间树或者解空间图,所以对于这个解空间树或图,我们就使用DFS策略了

    //choicelist:表示可以进行选择的列表,相当于树中可选用左节点还是右结点
    //track:表示为决策路径,相当于在树中为根节点到某个叶子节点的路径
    //result:表示存放根节点到所有叶子节点的所有路径
    backtrack(choicelist,track,result)
    {
    	if(track is ok)result.push(track);
    	else{
    		for choice in choicelist:
    			//choose过程:选择一个choice加入track,相当于在树中选择一个节点加入路径
    			backtrack(choices,track,result);//进入下一步决策
    			//unchoose过程:从track中撤销上面的选择,相当于在树中移除上一次选择的节点
    	}
    }
    

    应用与风险:
    1)DFS多用于连通性问题因为其运行思想与人脑的思维很相似,故解决连通性问题更自然。
    2)BFS多用于解决最短路问题,其运行过程中需要储存每一层的信息,所以其运行时需要储存的信息量较大,如果人脑也可储存大量信息的话,理论上人脑也可运行BFS。
    3)多数情况运行BFS所需的内存会大于DFS需要的内存(DFS一次访问一条路,BFS一次访问多条路)
    4)风险:DFS容易爆栈(栈不易”控制”),BFS通过控制队列可以很好解决”爆队列”风险。

    原文链接:https://blog.csdn.net/qq_43152052/article/details/99229105

  • 相关阅读:
    [Leetcode]@python 89. Gray Code
    [Leetcode]@python 88. Merge Sorted Array.py
    [Leetcode]@python 87. Scramble String.py
    [Leetcode]@python 86. Partition List.py
    [leetcode]@python 85. Maximal Rectangle
    0523BOM
    0522作业星座
    0522dom
    0520
    0519作业
  • 原文地址:https://www.cnblogs.com/xym4869/p/12575831.html
Copyright © 2020-2023  润新知