1 DFS

1 定义:

深度优先搜索算法(英语:Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。

深度优先搜索是图论中的经典算法,利用深度优先搜索算法可以产生目标图的相应拓扑排序表,利用拓扑排序表可以方便的解决很多相关的图论问题,如最大路径问题等等。

  • 北京大学: 将问题各状态之间的状态转移关系描述成一个图, DFS遍历图的框架

2 按照 1-2-3-4-5 的顺序来比较不同的策略。

DFS与BFS - 图1


  1. ![](https://cdn.nlark.com/yuque/0/2019/png/388749/1570352633741-1001b361-14fe-4a08-99ef-6546384465d7.png#align=left&display=inline&height=198&margin=%5Bobject%20Object%5D&originHeight=198&originWidth=331&size=0&status=done&style=none&width=331)

而深度优先搜索在得到一个新节点时立即对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。
从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种 可达性 问题。
在程序实现 DFS 时需要考虑以下问题:

图中

  • 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。
  • 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。

    二叉树中

  • 栈: 用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。

  • 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记, 递归向stack 加入左右子节点, 因为子节点不会向 上访问, 变形得标记了节点。

1 二叉树的最大深度

给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。

  • idea: pair 保存节点和深度的值 ```java import javafx.util.Pair; import java.lang.Math;

class Solution { public int maxDepth(TreeNode root) { Queue> stack = new LinkedList<>(); if (root != null) { stack.add(new Pair(root, 1)); }

int depth = 0;
while (!stack.isEmpty()) {
  Pair<TreeNode, Integer> current = stack.poll();
  root = current.getKey();
  int current_depth = current.getValue();
  if (root != null) {
    depth = Math.max(depth, current_depth);
    stack.add(new Pair(root.left, current_depth + 1));
    stack.add(new Pair(root.right, current_depth + 1));
  }
}
return depth;

} };


```java
class Solution {
  public int maxDepth(TreeNode root) {
   int left = maxDepth(root.left);
   int right = maxDepth(root.right);
   return Math.max(left, right)+1;
  }
};

2 在每个树行中找最大值

您需要在二叉树的每一行中找到最大的值。

示例:
输入:

      1<br />         / \<br />        3   2<br />       / \   \  <br />      5   3   9 

输出: [1, 3, 9]

  • 类似: 右视图, 每一行的值。
    /**
    * Definition for a binary tree node.
    * public class TreeNode {
    *     int val;
    *     TreeNode left;
    *     TreeNode right;
    *     TreeNode(int x) { val = x; }
    * }
    */
    class Solution {
      public List<Integer> largestValues(TreeNode root) {
          Queue<TreeNode> queue = new LinkedList<>();
          List<Integer>  res = new LinkedList<>();
          if(root ==null){
              return res;
          }
          queue.add(root);
          while(!queue.isEmpty()){
              // 比普通bfs多了一层循环, 找出同层的树的最小值
              int maxValue = Integer.MIN_VALUE;
              int count = queue.size();
              while(count-- > 0){
                  // 用count来控制一层循环。
                  TreeNode temNode = queue.poll();
                  if(temNode.left != null){
                      queue.add(temNode.left);
                  }
                  if(temNode.right != null){
                      queue.add(temNode.right);
                  }
                  maxValue = Math.max(maxValue, temNode.val);
              }
              res.add(maxValue);
          }
          return res;
      }
    }
    

3 路径总和

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2

class Solution {
  public boolean hasPathSum(TreeNode root, int sum) {
    if (root == null)
      return false;


    // 用 sum_stack 与 node_stack 保持同样的同点同值。
    LinkedList<TreeNode> node_stack = new LinkedList();
    LinkedList<Integer> sum_stack = new LinkedList();
    node_stack.add(root);
    sum_stack.add(sum - root.val);

    TreeNode node;
    int curr_sum;
    while ( !node_stack.isEmpty() ) {
      node = node_stack.pollLast();
      curr_sum = sum_stack.pollLast();
      if ((node.right == null) && (node.left == null) && (curr_sum == 0))
        return true;

      if (node.right != null) {
        node_stack.add(node.right);
        sum_stack.add(curr_sum - node.right.val);
      }
      if (node.left != null) {
        node_stack.add(node.left);
        sum_stack.add(curr_sum - node.left.val);
      }
    }
    return false;
  }
}
  • 递归
class Solution {
  public boolean hasPathSum(TreeNode root, int sum) {
    if (root == null)
      return false;

    sum -= root.val;
    if ((root.left == null) && (root.right == null))
      return (sum == 0);
    return hasPathSum(root.left, sum) || hasPathSum(root.right, sum);
  }
}

2 BFS

  • 广度优先搜索一层一层地进行遍历,每层遍历都以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点。需要注意的是,遍历过的节点不能再次被遍历。

图中

  • 队列:用队列来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归队列。
  • 标记:和DFS 一样同样需要对已经遍历过的节点进行标记。

树中

  • 队列:用队列来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归队列。
  • 标记:用node.left, node.right向queue中加入节点的最近子节点。
  • 相当与mark,不能向上访问父节点, 只能访问子节点。

1 N叉树的层序遍历

给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。
例如,给定一个 3叉树 :

DFS与BFS - 图2

返回其层序遍历:
[
[1],
[3,2,4],
[5,6]
]

  • idea: 用queue.size()来控制一层的循环

    /*
    // Definition for a Node.
    class Node {
      public int val;
      public List<Node> children;
    
      public Node() {}
    
      public Node(int _val,List<Node> _children) {
          val = _val;
          children = _children;
      }
    };
    */
    class Solution {
      public List<List<Integer>> levelOrder(Node root) {
          List<List<Integer>> res= new ArrayList<>();
          if(root == null) return res;
          Queue<Node> queue= new LinkedList<>();
    
          queue.add(root);
          while(!queue.isEmpty()){
              List<Integer> list = new ArrayList<>();
              int cnt = queue.size();
    
              while(cnt-- > 0){
                  Node cur = queue.poll();
                  list.add(cur.val);
                  for(Node x: cur.children){
                      if(x != null){
                          queue.add(x);
                      }
    
                  }
              }
              res.add(list);
          }
          return res;
      }
    }
    

2 对称二叉树

给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3

  • idea: 每一层都o
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
   public boolean isSymmetric(TreeNode root) {
    Queue<TreeNode> q = new LinkedList<>();
    q.add(root);
    q.add(root);
    while (!q.isEmpty()) {
        TreeNode t1 = q.poll();
        TreeNode t2 = q.poll();
        if (t1 == null && t2 == null) continue;
        if (t1 == null || t2 == null) return false;
        if (t1.val != t2.val) return false;

       // 树节点加入队列顺序, 使得每次都可以比较对称树节点的值。
        q.add(t1.left);
        q.add(t2.right);
        q.add(t1.right);
        q.add(t2.left);
    }
    return true;
}
}

3 课程表

现在你总共有 n 门课需要选,记为 0n-1
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]
给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习?
示例 1:
输入: 2, [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
示例 2:
输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。

3 DFS&&BFS 双解法

1 岛屿数量

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

示例 2:
输入:
11000
11000
00100
00011
输出: 3

  • 深度优先搜索, 查看当前节点是否为 “1”, 小岛数量加一, 设当前节点为”0”,
  • 利用是DFS搜索当前节点四个方向的节点
  • 出口条件是, if(r <0 || c < 0 || r >=nr || c >= nc || grid[r][c] == ‘0’)
class Solution {
  void dfs(char[][] grid, int r, int c) {
    int nr = grid.length;
    int nc = grid[0].length;

    if(r <0 || c < 0 || r >=nr || c >= nc || grid[r][c] == '0'){
        return;
    }
      grid[r][c] ='0';

      dfs(grid, r-1, c);
      dfs(grid, r+1, c);
      dfs(grid, r, c-1);
      dfs(grid, r, c+1);
  }

  public int numIslands(char[][] grid) {
    if (grid == null || grid.length == 0) {
      return 0;
    }

    int nr = grid.length;
    int nc = grid[0].length;
    int num_islands = 0;
    for (int r = 0; r < nr; ++r) {
      for (int c = 0; c < nc; ++c) {
        if (grid[r][c] == '1') {
          ++num_islands;
          dfs(grid, r, c);
        }
      }
    }

    return num_islands;
  }
}