【算法】DFS总结

353 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

一、前言

BFSBreath First Search,广度优先搜索)和DFSDepth First Search,深度优先搜索)是特别常用的两种算法。

  • DFS 算法的空间复杂度是 递归堆栈
  • DFS算法其实可以被认为是 回溯算法

DFS 模板如下: 参考回溯。

result = []; // 结果void backtrack(路径, 选择列表) {
    if (满足结束条件) {
        result.add(路径);
        return;
    }
    
    for (选择 : 选择列表) {
        // 1. 做选择
        // 2. 调用
        backtrack(路径, 选择列表);
        // 3. 撤销选择
    }
}

二、题目

(1)二叉树中的所有路径(易)

LeetCode 257

题干分析

这个题目说的是,给你一棵二叉树,你要返回所有从根到叶子节点的路径。

# 比如说,给你的二叉树是:
   1
 /   \
2     4
 \
  8
​
# 在这棵二叉树中,从根到叶子节点有两条路径:
[
 "1->2->8",
 "1->4"
]
​

思路解法

思路:DFS、回溯

  • 用字符串保存遍历的路径。(字符串拼接存在拷贝的过程,即遍历中存在大量拷贝行为)

复杂度计算:

DFS-2022-08-0908-42-35.png

// Time: O(n^2), Space: O(n^2), Faster: 32.55%
public List<String> binaryTreePaths(TreeNode root) {
    if (null == root) return Collections.emptyList();
    List<String> result = new ArrayList<>();
    dfs(result, root, "");
    return result;
}
​
private void dfs(List<String> result, TreeNode root, String path) {
    if (null == root) {
        return;
    }
​
    if ("".equals(path)) {
        path += root.val;
    } else {
        path += "->" + root.val;
    }
​
    if (null == root.left && null == root.right) {
        result.add(path);
        return;
    }
​
    dfs(result, root.left, path);
    dfs(result, root.right, path);
}

优化: 使用 StringBuilder 减少字符串拷贝。

// Time: O(n*log(n)), Space: O(n), Faster: 99.97%
public List<String> binaryTreePathsV2(TreeNode root) {
    List<String> result = new ArrayList<>();
    dfsV2(root, new StringBuilder(), result);
    return result;
}
​
private void dfsV2(TreeNode root, StringBuilder path, List<String> result) {
    if (root == null) return;
    int len = path.length();
    path.append(root.val);
    if (root.left == null && root.right == null) {
        result.add(path.toString());
    } else {
        path.append("->");
        dfsV2(root.left, path, result);
        dfsV2(root.right, path, result);
    }
    path.setLength(len); // 重点,限制长度,可以认为是回溯
}

(2)二叉树的右视图(中)

LeetCode 199

题干分析

这个题目说的是,给你一棵二叉树,并且你站在这棵树的右边,你要返回从上到下看到的节点值。

# 比如说,给你的二叉树是:
​
     1
   /   \
  2     4
 / \
6   8# 站在这棵二叉树的右边看过来,从上到下看到的数字依次是:
[1, 4, 8]

思路解法

思路有二: BFSDFS

方法一:BFS

  • 层序遍历顺序:从左到右,一个个入队。
  • 输出每一层中队列的最后一个元素即可。

BFS-2022-08-0821-07-59.png

// 方法一: BFS
// Time: O(n), Space: O(n), Faster: 82.03%
public List<Integer> rightSideViewBFS(TreeNode root) {
    if (null == root) return Collections.emptyList();
    Queue<TreeNode> queue = new LinkedList<>();
    List<Integer> result = new ArrayList<>();
    queue.add(root);
    while (!queue.isEmpty()) {
        int size = queue.size();
        TreeNode node = null;
        while (size-- > 0) {
            node = queue.poll();
            if (null != node.left) queue.add(node.left);
            if (null != node.right) queue.add(node.right);
        }
        result.add(node.val);
    }
    return result;
}

方法二:DFS

  • 注意递归方向: 先右子树,再左子树。

DFS-2022-08-0821-20-51.png

// 方法二:DFS
// Time: O(n), Space: O(n), Faster: 100.00%
public List<Integer> rightSideViewDFS(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    dfs(root, result, 0);
    return result;
}
​
private void dfs(TreeNode root, List<Integer> result, int level) {
    if (root == null) return;
    if (level == result.size()) result.add(root.val);
    dfs(root.right, result, level + 1);
    dfs(root.left, result, level + 1);
}