BFS/DFS

树专题回溯算法中其实已经涉及到了BFS和DFS算法,这里单独提出再进一步学习一下

BFS

广度优先遍历 Breadth-First-Search

这部分的内容也主要是学习了labuladong公众号内的相关讲解

算法流程

  1. 首先将开始节点放入队列中。
  2. 从队列中取出第一个节点,并检验它是否为目标。
    • 如果找到目标,则结束搜索并回传结果。
    • 否则将它所有尚未检验过的直接子节点加入队列中。
  3. 若队列为空,表示整张图都检查过了——亦即图中没有欲搜索的目标。结束搜索并回传“找不到目标”。
  4. 重复步骤 2。

算法模版

  1. 一般模版:

    1. void bfs(Node start, Node target){
    2. // 使用双端队列,而不是数组
    3. Queue<TreeNode> queue = new ArrayDeque<>();
    4. // 注意:ArrayDeque不允许null值,LinkedList允许null值
    5. // Queue<TreeNode> queue = new LinkedList<>();
    6. // 记录层数
    7. int steps = 0;
    8. // 记录访问过的节点
    9. Set<Node> visited = new HashSet<>();
    10. queue.offer(start);
    11. while(!queue.isEmpty()){
    12. // 当前层的节点数
    13. int size = queue.size();
    14. // 遍历当前层的所有节点数
    15. for (int i=0; i<size; i++){
    16. Node node = queue.poll();
    17. result.add(node);
    18. // 判断节点是否满足,而决定是否返回等操作
    19. if(node.val == target.val){
    20. return steps; // || return result;
    21. }
    22. // 将node周围的还未访问过的节点都加入队列中
    23. for(Node tmp: node.adj()){
    24. if(!visited.contains(tmp)){
    25. queue.offer(tmp);
    26. visited.add(tmp);
    27. }
    28. }
    29. }
    30. steps += 1; // 遍历完一层,层数+1
    31. }
    32. return;
    33. }

    相关题目:

  2. 针对树这种数据结构,因没有子节点回指向父节点的指针,因此可以不需要上述的 visited

    1. void bfs(TreeNode root){
    2. // 使用双端队列,而不是数组
    3. Queue<TreeNode> queue = new ArrayDeque<>();
    4. // 注意:ArrayDeque不允许null值,LinkedList允许null值
    5. // Queue<TreeNode> queue = new LinkedList<>();
    6. // 记录层数
    7. int steps = 0;
    8. queue.offer(root);
    9. while(!queue.isEmpty()){
    10. // 当前层的节点数
    11. int size = queue.size();
    12. // 遍历当前层的所有节点数
    13. for (int i=0; i<size; i++){
    14. TreeNode node = queue.poll();
    15. result.add(node);
    16. // 判断节点是否满足,而决定是否返回等操作
    17. if(node.left != null){
    18. queue.offer(node.left);
    19. }
    20. if(node.right != null){
    21. queue.offer(node.right);
    22. }
    23. }
    24. steps += 1; // 遍历完一层,层数+1
    25. }
    26. return;
    27. }

    典型题目:

带权最短距离

堆专题中涉及到了带权的最短距离,即此时的节点到邻居之间的距离不是定值了,而是带有权重。

使用优先队列的 BFS 实现典型的就是 dijkstra 算法。dijkstra 算法主要解决的是图中任意两点的最短距离。

算法的基本思想是贪心,每次都遍历所有邻居,并从中找到距离最小的,本质上是一种广度优先遍历。

更具体的内容跳转:堆专题-总结-四大应用-带权最短距离

DFS

深度优先遍历 Depth-First-Search,DFS,是一种用于遍历或搜索树或图的算法。

算法流程:

  1. 首先将根节点放入stack 中。
  2. stack 中取出第一个节点,并检验它是否为目标。如果找到所有的节点,则结束搜寻并回传结果。否则将它某一个尚未检验过的直接子节点加入stack中。
  3. 重复步骤 2。
  4. 如果不存在未检测过的直接子节点。将上一级节点加入stack中,重复步骤 2。
  5. 重复步骤 4。
  6. stack为空,表示整张图都检查过了——亦即图中没有欲搜寻的目标。结束搜寻并回传“找不到目标”。

这里的 stack 可以理解为自己实现的栈,也可以理解为调用栈。如果是调用栈的时候就是递归,如果是自己实现的栈的话就是迭代。

算法模版

一个典型的通用的 DFS 模板可能是这样的:

  1. boolean[] visited;
  2. void dfs(int i) {
  3. if (满足特定条件){
  4. // 返回结果 or 退出搜索空间
  5. }
  6. visited[i] = true // 将当前状态标为已搜索
  7. for (根据i能到达的下个状态j) {
  8. if (!visited[j]) { // 如果状态j没有被搜索过
  9. dfs(j)
  10. }
  11. }
  12. }

这部分内容在可以参考树专题回溯算法内容

题目

752. 打开转盘锁

  1. public int openLock(String[] deadends, String target) {
  2. // 记录需要跳过的死亡密码
  3. Set<String> deads = new HashSet<>();
  4. for(String s: deadends){
  5. deads.add(s);
  6. }
  7. // 记录已经穷举过的密码,防止走回头路
  8. Set<String> visited = new HashSet<>();
  9. // 队列
  10. Queue<String> queue = new LinkedList<>();
  11. // 从起点开始进行BFS
  12. int step = 0;
  13. queue.offer("0000");
  14. visited.add("0000");
  15. while(!queue.isEmpty()){
  16. // 当前层的节点数
  17. int size = queue.size();
  18. // 遍历当前层的所有节点数
  19. for(int i=0; i<size; i++){
  20. String cur = queue.poll();
  21. // 判断节点是否相应条件
  22. if(deads.contains(cur)) continue;
  23. if(cur.equals(target)) return step;
  24. // 周围的还未访问过的可能都加入队列中
  25. for(int j=0; j<4; j++){
  26. // 向上拨一个数字
  27. String up = plusOne(cur, j);
  28. if (!visited.contains(up)) {
  29. queue.offer(up);
  30. visited.add(up);
  31. }
  32. // 向下拨一个数字
  33. String down = minusOne(cur, j);
  34. if (!visited.contains(down)) {
  35. queue.offer(down);
  36. visited.add(down);
  37. }
  38. }
  39. }
  40. step += 1; // 遍历完一层,层数+1
  41. }
  42. // 如果穷举完都没找到目标密码,那就是找不到了
  43. return -1;
  44. }
  45. // 将 s[j] 向上拨动一次
  46. private String plusOne(String s, int j) {
  47. char[] ch = s.toCharArray();
  48. if (ch[j] == '9')
  49. ch[j] = '0';
  50. else
  51. ch[j] += 1;
  52. return new String(ch);
  53. }
  54. // 将 s[i] 向下拨动一次
  55. private String minusOne(String s, int j) {
  56. char[] ch = s.toCharArray();
  57. if (ch[j] == '0')
  58. ch[j] = '9';
  59. else
  60. ch[j] -= 1;
  61. return new String(ch);
  62. }

参考:BFS 算法框架套路详解

773. 滑动谜题

  1. public int slidingPuzzle(int[][] board) {
  2. /******* 准备工作 *******/
  3. int m = 2, n = 3;
  4. char[] start = new char[6];
  5. char[] target = {'1', '2', '3', '4', '5', '0'};
  6. // 将2*3转化为字符串
  7. int index_s = 0;
  8. for (int i = 0; i < m; i++) {
  9. for (int j = 0; j < n; j++) {
  10. start[index_s++] = (char)(board[i][j] + '0');
  11. }
  12. }
  13. // 记录一维字符串的相邻索引
  14. List<List<Integer>> neighbor = new ArrayList<>();
  15. neighbor.add(Arrays.asList(1, 3));
  16. neighbor.add(Arrays.asList(0, 2, 4));
  17. neighbor.add(Arrays.asList(1, 5));
  18. neighbor.add(Arrays.asList(0, 4));
  19. neighbor.add(Arrays.asList(1, 3, 5));
  20. neighbor.add(Arrays.asList(2, 4));
  21. /******* BFS 算法框架开始 *******/
  22. Queue<char[]> queue = new LinkedList<>();
  23. Set<String> visited = new HashSet<>();
  24. queue.offer(start);
  25. visited.add(new String(start));
  26. int steps = 0;
  27. while(!queue.isEmpty()){
  28. int size = queue.size();
  29. for(int i=0; i<size; i++){
  30. char[] cur = queue.poll();
  31. // 判断是否达到目标局面
  32. if(isEqual(cur, target)){
  33. return steps;
  34. }
  35. // 找到数字 0 的索引
  36. int index = 0;
  37. for(; cur[index] != '0'; index++);
  38. // 将数字 0 和相邻的数字交换位置
  39. for(Integer adj: neighbor.get(index)){
  40. char[] tmp = new char[6];
  41. System.arraycopy(cur, 0, tmp, 0, 6);
  42. swap(tmp, index, adj);
  43. if(!visited.contains(new String(tmp))){
  44. queue.offer(tmp);
  45. visited.add(new String(tmp));
  46. }
  47. }
  48. }
  49. steps++;
  50. }
  51. return -1;
  52. }
  53. private void swap(char[] chars, int i, int j){
  54. char tmp = chars[i];
  55. chars[i] = chars[j];
  56. chars[j] = tmp;
  57. }
  58. private boolean isEqual(char[] a, char[] b){
  59. if(a.length != b.length){
  60. return false;
  61. }
  62. for(int i=0; i<a.length; i++){
  63. if(a[i] != b[i]){
  64. return false;
  65. }
  66. }
  67. return true;
  68. }

参考:益智游戏克星:BFS暴力搜索算法

111. 二叉树的最小深度

  1. public int minDepth(TreeNode root) {
  2. if(root == null){
  3. return 0;
  4. }
  5. return bfs(root);
  6. }
  7. private int bfs(TreeNode root){
  8. Queue<TreeNode> queue = new LinkedList<>();
  9. // 记录层数
  10. int steps = 1;
  11. queue.offer(root);
  12. while(!queue.isEmpty()){
  13. // 当前层的节点数
  14. int size = queue.size();
  15. // 遍历当前层的所有节点数
  16. for (int i=0; i<size; i++){
  17. TreeNode node = queue.poll();
  18. // 判断节点是否满足,而决定是否返回等操作
  19. if(node.left == null && node.right == null){
  20. return steps;
  21. }
  22. if(node.left != null){
  23. queue.offer(node.left);
  24. }
  25. if(node.right != null){
  26. queue.offer(node.right);
  27. }
  28. }
  29. steps += 1;
  30. }
  31. return steps;
  32. }

LeetCode:BFS/DFS的更多相关文章

  1. 经典图算法Java代码实践:BFS,DFS以及几种最短路径算法

    public class City { String name; int id; static int idCounter = 0; public City(String name) { this.n ...

  2. 【LeetCode】BFS || DFS [2017.04.10--2017.04.17]

    [102] Binary Tree Level Order Traversal [Medium-Easy] [107] Binary Tree Level Order Traversal II [Me ...

  3. 算法基础:BFS和DFS的直观解释

    算法基础:BFS和DFS的直观解释 https://cuijiahua.com/blog/2018/01/alogrithm_10.html 一.前言 我们首次接触 BFS 和 DFS 时,应该是在数 ...

  4. Leetcode题目200.岛屿数量(BFS+DFS+并查集-中等)

    题目描述: 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量.一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的.你可以假设网格的四个边均被水包围. 示例 ...

  5. LeetCode:二叉树剪枝【814】

    LeetCode:二叉树剪枝[814] 题目描述 给定二叉树根结点 root ,此外树的每个结点的值要么是 0,要么是 1. 返回移除了所有不包含 1 的子树的原二叉树. ( 节点 X 的子树为 X ...

  6. 【LeetCode】BFS(共43题)

    [101]Symmetric Tree 判断一棵树是不是对称. 题解:直接递归判断了,感觉和bfs没有什么强联系,当然如果你一定要用queue改写的话,勉强也能算bfs. // 这个题目的重点是 比较 ...

  7. LeetCode:“剑指 Offer”

    LeetCode:"剑指 Offer" 刷题小菜鸡,花了几天时间做了一遍 LeetCode 上给出的 "剑指 Offer" 在此做一下记录 LeetCode主页 ...

  8. LeetCode:树专题

    树专题 参考了力扣加加对与树专题的讲解,刷了些 leetcode 题,在此做一些记录,不然没几天就没印象了 力扣加加-树专题 总结 树的定义 // Definition for a binary tr ...

  9. BFS/DFS算法介绍与实现(转)

    广度优先搜索(Breadth-First-Search)和深度优先搜索(Deep-First-Search)是搜索策略中最经常用到的两种方法,特别常用于图的搜索.其中有很多的算法都用到了这两种思想,比 ...

随机推荐

  1. openswan协商流程之(五):main_inR2_outI3()

    主模式第五包:main_inR2_outI3 文章目录 主模式第五包:main_inR2_outI3 1. 序言 2.函数调用关系 3. 第五个报文流程图 4. main_inR2_outI3()源码 ...

  2. CommonsCollections1 反序列化利用链分析

    InvokerTransformer 首先来看 commons-collections-3.1-sources.jar!\org\apache\commons\collections\functors ...

  3. 聊聊ReentrantLock基于AQS的公平锁和非公平锁的实现区别

    ReentrantLock锁的实现是基于AQS实现的,所以先简单说下AQS: AQS是AbstractQueuedSynchronizer缩写,顾名思义:抽象的队列同步器,它是JUC里面许多同步工具类 ...

  4. Java环境搭建与HelloWprld—改变世界的第一步

    1. JDK下载 访问oracle官网:http://www.oracle.com 在首页点击Downloads,进入oracle软件下载页. 在下载页面,点击Java. 选择Java (JDK) f ...

  5. 使用 mysql 的 Docker 镜像

    使用 mysql 的 Docker 镜像 前言 之前搞了很多都是手工在操作系统的镜像中安装使用 mysql,作为自己折腾也就算了,作为实际使用实为不妥:Docker最重要的特性就是可扩展性,把各种程序 ...

  6. 深度学习——前向传播算法和反向传播算法(BP算法)及其推导

    1 BP算法的推导 图1 一个简单的三层神经网络 图1所示是一个简单的三层(两个隐藏层,一个输出层)神经网络结构,假设我们使用这个神经网络来解决二分类问题,我们给这个网络一个输入样本,通过前向运算得到 ...

  7. C++快速读入

    使用C++的标准cin进行读入速度比较慢,尤其是在大数据的情况下,所以我们需要使用一种方法,按照字符读入,最后再"组装"成整数.由于字符读入比数字要快,所以这样做可以提高读入速度. ...

  8. 事项同步事项编码(mt_code)长度超过数据库限制的varchar(32)线上问题

    改下长度限制重新同步下,可以恢复正常!

  9. js 模板方法模式

    * 分离出共同点 function Beverage() {} Beverage.prototype.boilWater = function() { console.log("把水煮沸&q ...

  10. python实现rtsp取流并截图

    import cv2 def get_img_from_camera_net(folder_path): cap = cv2.VideoCapture("rtsp://admin:admin ...