zoukankan      html  css  js  c++  java
  • 广度优先搜索

    即BFS
    BFS 的核心思想就是把一些问题抽象成图,从一个点开始,向四周开始扩散。一般来说,我们写 BFS 算法都是用「队列」这种数据结构,每次将一个节点周围的所有节点加入队列。

    BFS 相对 DFS 的最主要的区别是:BFS 找到的路径一定是最短的,但代价就是空间复杂度比 DFS 大很多

    框架
    BFS 出现的常见场景:让你在一幅「图」中找到从起点start到终点target的最近距离,这个例子听起来很枯燥,但是 BFS 算法问题其实都是在干这个事儿。
    这个广义的描述可以有各种变体,比如走迷宫,有的格子是围墙不能走,从起点到终点的最短距离是多少?如果这个迷宫带「传送门」可以瞬间传送呢?再比如说两个单词,要求你通过某些替换,把其中一个变成另一个,每次只能替换一个字符,最少要替换几次?
    本质上就是一幅「图」,让你从一个起点,走到终点,问最短路径。这就是 BFS 的本质。

    // 计算从起点 start 到终点 target 的最近距离
    int BFS(Node start, Node target) {
        Queue<Node> q; // 核心数据结构
        Set<Node> visited; // 避免走回头路
    
        q.offer(start); // 将起点加入队列
        visited.add(start);
        int step = 0; // 记录扩散的步数
    
        while (q not empty) {
            int sz = q.size();
            /* 将当前队列中的所有节点向四周扩散 */
            for (int i = 0; i < sz; i++) {
                Node cur = q.poll();
                /* 划重点:这里判断是否到达终点 */
                if (cur is target)
                    return step;
                /* 将 cur 的相邻节点加入队列 */
                for (Node x : cur.adj())
                    if (x not in visited) {
                        q.offer(x);
                        visited.add(x);
                    }
            }
            /* 划重点:更新步数在这里 */
            step++;
        }
    }
    

    bfs的本质是在“图”中的扩散式搜索,每一步都会把当前队列中的元素 的 周围的元素都收入队列,从搜索的大框架来说,从最开始只往队列中加入一个元素起,每一步的操作都是依次把队列中的元素弹出(返回并删除该元素),对该元素进行比对(若是target则返回),若不是target则把这个元素的相邻元素全部压入队列之中(事实上并不是全部,为了避免重复操作,必须在这一步把已操作过的元素略过)。
    一般采用队列进行装载,采用hashset来记录是否访问过。
    正如上面所说,bfs解决的是从起点到终点的最短路径,那么使用bfs前一定要先想明白起点和终点是什么。

    实例1:
    leet111.二叉树的最小深度
    本题很简单,起点是root,终点是叶子结点。
    叶子节点的判断条件就是左右节点都是null,这里还有个点就是二叉树是不能走回头路的,也就是说不会出现已访问过的元素,那么就无需设置visited。

    class Solution {
        public int minDepth(TreeNode root) {
            if(root==null){return 0;}
            int step=1;
            Queue<TreeNode> q=new LinkedList<TreeNode>();
    
            q.offer(root);
    
            while(!q.isEmpty()){
                int sz=q.size();
    
                for(int i=0;i<sz;i++){
                    TreeNode curr=q.poll();
                    if(curr.left==null&&curr.right==null){
                        return step;
                    }
    
                    if(curr.left!=null){
                        q.offer(curr.left);
                    }
                    if(curr.right!=null){
                        q.offer(curr.right);
                    }
                }
                step++;
            }
            return -1;
        }
    }
    

    本题就是按照框架来。
    bfs用在二叉树上,那就是每次搜索收入一层的节点,然后依次比对。

    实例2:
    leet752.打开转盘锁
    本题多了一个deadends,但也可以用一个hashset去收集,然后比对,问题不大。
    找相邻的元素,也就是往上/往下旋转按钮,可以通过char[] 与 string的变换来操作。
    其余的还是用框架。

    class Solution {
        public int openLock(String[] deadends, String target) {
            Queue<String> q=new LinkedList<String>();
            Set<String> dead=new HashSet<>();
            for(String s:deadends){dead.add(s);}
            Set<String> visited=new HashSet<>();
    
            q.offer("0000");
            visited.add("0000");
    
            int step=0;
    
            while(!q.isEmpty()){
                int sz=q.size();
    
                for(int i=0;i<sz;i++){
                    String curr=q.poll();
                    if(dead.contains(curr)){
                        continue;
                    }
                    if(curr.equals(target)){
                        return step;
                    }
    
                    for(int j=0;j<4;j++){
                        if(!visited.contains(up(curr,j))){
                            q.offer(up(curr,j));
                            visited.add(up(curr,j));
                        }
    
                        if(!visited.contains(down(curr,j))){
                            q.offer(down(curr,j));
                            visited.add(down(curr,j));
                        }
                    }
                }
                step++;
            }
            return -1;
    
        }
    
        String up(String s,int j){
            char[] t=s.toCharArray();
            if(t[j]=='9'){t[j]='0';}
            else{
                t[j]+=1;
            }
            return new String(t);
        }
    
        String down(String s,int j){
            char[] t=s.toCharArray();
            if(t[j]=='0'){t[j]='9';}
            else{
                t[j]-=1;
            }
            return new String(t);
        }
    }
    

    BFS 的逻辑,depth每增加一次,队列中的所有节点都向前迈一步,这保证了第一次到达终点的时候,走的步数是最少的。而DFS 实际上是靠递归的堆栈记录走过的路径,你要找到最短路径,肯定得把二叉树中所有树杈都探索完才能对比出最短的路径有多长。BFS 借助队列做到一次一步「齐头并进」,是可以在不遍历完整棵树的条件下找到最短距离的。形象点说,DFS 是线,BFS 是面;DFS 是单打独斗,BFS 是集体行动。BFS 可以找到最短距离,但是空间复杂度高,而 DFS 的空间复杂度较低。

  • 相关阅读:
    Java HashMap存储问题
    <转>堆和栈的区别
    Linux shell命令
    DNS(三)DNS SEC(域名系统安全扩展)
    DNS (二)协议
    绕过CDN查找网站真实IP方法
    stream流思想应用
    http接口实现附件对接
    AQS深入分析
    AQS快速入门
  • 原文地址:https://www.cnblogs.com/shiji-note/p/14461554.html
Copyright © 2011-2022 走看看