LeetCode刷题:BFS

引导

讲解
BFS与DFS对比

DFS关键字:递归/栈 + 所有解
DFS实现重要依赖于堆栈/递归 ,较为简单的解决了如何遍历所有元素,以及寻求“终点”的问题。
但是,DFS虽然可以查找到到达路径,但是却找不到最短的路径,针对这一问题,给出了BFS(广度优先遍历)的算法。
与DFS不同的是,这次不再是每个分叉路口一个一个走了,而是全部,同时遍历,直到找到终点,所对应的“层数”便是最短路径所需要的步数
上述解法当中,如果需要对当前队列中所有元素处理(即需要有层次的概念时),需要加入for(i<size)的循环
如果一次只处理队列中的一个元素(无层次概念),可以不用加for循环

BFS通用解法

int BFS(Node root, Node target) {
    
    
    Queue<TreeNode> queue = new ArrayDeque<>();  // 建立队列
    int step = 0;       // 建立行动步数,通常用这个作为最短路径的返回值
    // initialize
    queue.add(root);
    // BFS
    while (!queue.isEmpty()) {
    
    
        step = step + 1;
        // 记录此时的队列大小,也即此层元素的多少
        int size = queue.size();
        for (int i = 0; i < size; ++i) {
    
     //遍历此层的所有元素
            Node cur = queue.poll();
            return step if cur is target;
            for (Node next : the neighbors of cur) {
    
    
                queue.offer(next);       //加入查找的方向,将下一层元素加入到队列中
            }
        }
    }
    return -1;          // 没有找到目标返回-1
}

例题

102,树的层次遍历:标准解法

在这里插入图片描述

参考:
https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/bfs-de-shi-yong-chang-jing-zong-jie-ceng-xu-bian-l/

class Solution {
    
    
    public List<List<Integer>> levelOrder(TreeNode root) {
    
    
        List<List<Integer>> res = new LinkedList<>();
        BFS(root, res);
        return res;
    }
 
    private void BFS(TreeNode treeNode, List<List<Integer>> res) {
    
    
        if (treeNode == null) {
    
    
            return;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(treeNode);
        while (!queue.isEmpty()) {
    
    
            List<Integer> level = new LinkedList<>();
            int n = queue.size(); // 由于下方queue会一直offer,所以这里只能int提前存储下size
            for(int i = 0; i < n; i++) {
    
     // 只是为了完成本层遍历,无实际作用
                TreeNode tmp = queue.poll();
                if(tmp != null) {
    
    
                    level.add(tmp.val);
                    queue.offer(tmp.left);
                    queue.offer(tmp.right);
                }
            }
            if(level.size() > 0) {
    
    
 res.add(level); // 由于上方的level每次都是新new的,因此无需下方这种写法
                // res.add(new LinkedList<>(level));  // 这种常常用于level是传入进来的参数,防止堆污染
            }
        }
    }
}

zigzag版本:双端队列
在这里插入图片描述

public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    
    
        List<List<Integer>> ans = new LinkedList<List<Integer>>();
        if (root == null) {
    
    
            return ans;
        }
 
        Queue<TreeNode> nodeQueue = new LinkedList<TreeNode>();
        nodeQueue.offer(root);
        boolean isOrderLeft = true;
 
        while (!nodeQueue.isEmpty()) {
    
    
            Deque<Integer> levelList = new LinkedList<Integer>();
            int size = nodeQueue.size();
            for (int i = 0; i < size; ++i) {
    
    
                TreeNode curNode = nodeQueue.poll();
                if (isOrderLeft) {
    
    
                    levelList.offerLast(curNode.val);
                } else {
    
    
                    levelList.offerFirst(curNode.val);
                }
                if (curNode.left != null) {
    
    
                    nodeQueue.offer(curNode.left);
                }
                if (curNode.right != null) {
    
    
                    nodeQueue.offer(curNode.right);
                }
            }
            ans.add(new LinkedList<Integer>(levelList));
            isOrderLeft = !isOrderLeft;
        }
 
        return ans;
    }

207. 课程表:拓扑排序

在这里插入图片描述
https://leetcode-cn.com/problems/course-schedule/
拓扑排序:
解答:
https://leetcode-cn.com/problems/course-schedule/solution/ke-cheng-biao-by-leetcode-solution/
参考文章:图的存储结构与拓扑排序实现思路
https://www.jianshu.com/p/cd24cfb6c8d0

DFS版本解答:

List<List<Integer>> edges;
    int[] visited; // 状态数组。0:未处理;1:正在搜索;2:已经完成
    boolean valid = true;  // 结果存储
 
    public boolean canFinish(int numCourses, int[][] prerequisites) {
    
    
        edges = new ArrayList<List<Integer>>();
        for (int i = 0; i < numCourses; ++i) {
    
    
            edges.add(new ArrayList<Integer>());
        }
        visited = new int[numCourses];
        for (int[] info : prerequisites) {
    
    
            edges.get(info[1]).add(info[0]); // 邻接表的构建,注意直接get.add
        }
        for (int i = 0; i < numCourses && valid; ++i) {
    
     // 剪枝
            if (visited[i] == 0) {
    
    
                dfs(i);
            }
        }
        return valid;
    }
 
    public void dfs(int u) {
    
    
        visited[u] = 1; // 对当前处理位置做标记
        for (int v: edges.get(u)) {
    
    
            if (visited[v] == 0) {
    
    
                dfs(v); // 所有的子节点都满足拓扑排序,则本次处理节点必然满足。
                if (!valid) {
    
    
                    return; // 直接返回,同时保留了当前为1的状态
                }
            } else if (visited[v] == 1) {
    
    
                valid = false;
                return;
            }
        }
        visited[u] = 2; // 当前处理完成:这里不再是清理标记而是替换为另一种状态
    }

BFS版本解答;

class Solution {
    
    
    List<List<Integer>> edges;
    int[] indeg; // 入度表
 
    public boolean canFinish(int numCourses, int[][] prerequisites) {
    
    
        edges = new ArrayList<List<Integer>>();
        for (int i = 0; i < numCourses; ++i) {
    
    
            edges.add(new ArrayList<Integer>());
        }
        indeg = new int[numCourses];
        for (int[] info : prerequisites) {
    
    
            edges.get(info[1]).add(info[0]);
            ++indeg[info[0]];
        }
 
        Queue<Integer> queue = new LinkedList<Integer>();
        for (int i = 0; i < numCourses; ++i) {
    
    
            if (indeg[i] == 0) {
    
    
                queue.offer(i); // 第一批入度为0的节点进入队列
            }
        }
 
        int visited = 0;
        while (!queue.isEmpty()) {
    
    
            ++visited;
            int u = queue.poll();
            for (int v: edges.get(u)) {
    
    
                --indeg[v];
                if (indeg[v] == 0) {
    
    
                    queue.offer(v); // 第二批入度为0的表进入队列
                }
            }
        }
 
        return visited == numCourses;
    }
}

994. 腐烂的橘子:多路BFS

https://leetcode-cn.com/problems/rotting-oranges/solution/fu-lan-de-ju-zi-by-leetcode-solution/

多源广度优先搜索

class Solution {
    
    
        // dr,dc 配合使用得到 grid[r][c] 上grid[r-1][c]左grid[r][c-1]下grid[r+1][c]右grid[r][c+1]的元素
        int[] dr = new int[]{
    
    -1, 0, 1, 0};  // 代指四个方向,左[-1,0],上[0,-1],右[1,0],下[0,1]
        int[] dc = new int[]{
    
    0, -1, 0, 1};
public int orangesRotting(int[][] grid) {
    
    
            // 获取二维数组的行数row 和 列数 column
            int R = grid.length, C = grid[0].length;
                          // queue : all starting cells with rotten oranges
            Queue<Integer> queue = new ArrayDeque();
            Map<Integer, Integer> depth = new HashMap();
            for (int r = 0; r < R; ++r)
                for (int c = 0; c < C; ++c)
                    if (grid[r][c] == 2) {
    
    
                        int code = r * C + c;  // 转化为索引唯一的一维数组
                        queue.add(code); //存储腐烂橘子
                        depth.put(code, 0); //存储橘子变为腐烂时的时间,key为橘子的一维数组下标,value为变腐烂的时间
                    }
 
                          int ans = 0; // 存储最终结果
            while (!queue.isEmpty()) {
    
     // 处理所有感染者
                int code = queue.remove();
                int r = code / C, c = code % C;
                for (int k = 0; k < 4; ++k) {
    
    
                    // grid位置获取方式,四个方向,四种策略
                    int nr = r + dr[k];
                    int nc = c + dc[k]; 
                    if (0 <= nr && nr < R && 0 <= nc && nc < C && grid[nr][nc] == 1) {
    
    
                                            // 0 <= nr && nr < R && 0 <= nc && nc < C,代指在grid有效范围之内,grid[nr][nc] == 1新鲜要处理
                        grid[nr][nc] = 2;
                        int ncode = nr * C + nc;
                        queue.add(ncode); // 入队
                        // 计次的关键 元素 grid[r][c] 的上左下右元素得腐烂时间应该一致
                        depth.put(ncode, depth.get(code) + 1);
                        ans = depth.get(ncode);
                    }
                }
            }
 
                          //检查grid,此时的grid能被感染已经都腐烂了,此时还新鲜的橘子无法被感染
            for (int[] row: grid)
                for (int v: row)
                    if (v == 1)
                        return -1;
            return ans;
}
    }

猜你喜欢

转载自blog.csdn.net/weixin_38370441/article/details/115249006
今日推荐