1.6 Depth First Search

碰到让你找所有方案的题,一定是DFS

90%DFS的题,要么是排列,要么是组合

• 什么时候用 DFS? • 求所有方案时

• 怎么解决DFS? • 不是排列就是组合

• 复杂度怎么算? • O(答案个数 * 构造每个答案的时间复杂度)

• 非递归怎么办? • 必“背”程序

4.1 组合搜索 Combination

问题模型:求出所有满足条件的“组合”。 判断条件:组合中的元素是顺序无关的。 时间复杂度:与 2^n 相关。

一般来说,如果面试官不特别要求的话,DFS都可以使用递归(Recursion)的方式来实现。 递归三要素是实现递归的重要步骤

子集Subsets

中文English

给定一个含不同整数的集合,返回其所有的子集

class Solution:
    #1. 递归的定义
    #以 S 开头的,配上 nums 以 index 开始的数所有组合放到 results 里
    def search(self, nums, S, index):
      #3. 递归的出口
        if index == len(nums):
            self.results.append(list(S)) #deep copy
            return
        #2. 递归的拆解 (如何进入下一层)
        #选了 nums[index]
        S.append(nums[index])
        self.search(nums, S, index + 1)
        #不选 nums[index]
        S.pop()
        self.search(nums, S, index + 1)

    def subsets(self, nums):
        self.results = []
        self.search(sorted(nums), [], 0)
        return self.results

数字组合

给定一个候选数字的集合 candidates 和一个目标值 target. 找到 candidates 中所有的和为 target 的组合.

在同一个组合中, candidates 中的某个数字不限次数地出现.

与 Subsets 比较

  • Combination Sum 限制了组合中的数之和 • 加入一个新的参数来限制

  • Subsets 无重复元素,Combination Sum 有重复元素 • 需要先去重

  • Subsets 一个数只能选一次,Combination Sum 一个数可以选很多次

    • 搜索时从 index 开始而不是从 index + 1

复杂度分析:

  • 时间复杂度:$O( n^(target/min) )$, $n$为集合中数字个数,$min$为集合中最小的数字

  • 每个位置可以取集合中的任意数字,最多有$target/min$个数字。

  • 空间复杂度:$O( n^(target/min) )$, $n$为集合中数字个数,$min$为集合中最小的数字

  • 对于用来保存答案的列表,最多有$n^(target/min)$种组合

数字组合 II

中文English

给定一个数组 num 和一个整数 target. 找到 num 中所有的数字之和为 target 的组合.

解释: 解集不能包含重复的组合

  1. 在同一个组合中, num 中的每一个数字仅能被使用一次

Palindrome Partitioning

使用 append + pop 的方式

有什么可以优化的地方? 判断回文串:dp

4.2 排列搜索 Permutation

问题模型:求出所有满足条件的“排列”。

判断条件:组合中的元素是顺序“相关”的。

时间复杂度:与 n! 相关。

permutation

给定一个数字列表,返回其所有可能的排列。(无重复数字)

Permutations II

和没有重复元素的 Permutation 一题相比,只加了两句话:

  1. Arrays.sort(nums) // 排序这样所有重复的数

  2. if (i > 0 && numsiarrow-up-right == numsi - 1arrow-up-right && !visitedi - 1arrow-up-right) { continue; } // 跳过会造成重复的情况

N Queens

用 visited 来标记 列号,横纵坐标之和,横纵坐标之差 有没有被用过

搜索,动态规划,二叉树的时间复杂度计算通用公式

搜索的时间复杂度:O(答案总数 构造每个答案的时间)` 举例:Subsets问题,求所有的子集。子集个数一共 2^n,每个集合的平均长度是 O(n) 的,所以时间复杂度为 O(n 2^n),同理 Permutations 问题的时间复杂度为:O(n * n!)

动态规划的时间复杂度:O(状态总数 * 计算每个状态的时间复杂度)` 举例:triangle,数字三角形的最短路径,状态总数约 O(n^2) 个,计算每个状态的时间复杂度为 O(1)——就是求一下 min。所以总的时间复杂度为 O(n^2)

用分治法解决二叉树问题的时间复杂度:O(二叉树节点个数 * 每个节点的计算时间)` 举例:二叉树最大深度。二叉树节点个数为 N,每个节点上的计算时间为 O(1)。总的时间复杂度为 O(N)

4.3 必“背”程序

Tree Traversal

http://www.jiuzhang.com/solutions/binary-tree-preorder-traversal/arrow-up-right

http://www.jiuzhang.com/solutions/binary-tree-inorder-traversal/arrow-up-right

http://www.jiuzhang.com/solutions/binary-tree-postorder-traversal/arrow-up-right

http://www.jiuzhang.com/solutions/binary-search-tree-iterator/arrow-up-right

Combination

http://www.jiuzhang.com/solutions/subsets/arrow-up-right

Permutation

http://www.jiuzhang.com/solutions/permutations/arrow-up-right

Last updated