- 有recursion一定有backtracking
- backtracking: 用recursion递归控制for循环嵌套的数量
- 所有的backtracking回溯算法都可以说是dfs深搜,所有的dfs深搜都可以说是recursion递归。backtracking回溯是更具体的一个分类。(backtracking子类,dfs父类,recursion祖父类)
- backtracking是纯暴力算法,在一些情况下比一层层for loop好
- 通常backtracking可以抽象为一棵n-ary tree n叉树。The execution of the backtracking is unfolded as a DFS traversal in a n-ary tree.
- Time Complexity: The total number of steps during the backtracking would be the number of nodes in the tree.
模板
- 用java存放结果时,要深拷贝,用new ArrayList<>(temp):Java传入的都是temp的地址,所以需要new ArrayList(temp) 这个相当于新创建了一个对象。如果不创建新对象, 我们放入多少个对象都是指向了temp一个对象。回溯的过程会不断remove元素, 浅拷贝的对象会跟着变化,因为地址一样, 所以大家得到的答案预期不一样。
- 深拷贝:完全赋值一份对象, 地址不同。复制一个一模一样的地址不一样的对象。
- 浅拷贝:地址一样, 操作同一个对象。默认放进去的对象就是一个浅拷贝。
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
StartIndex?
-
Combinations 组合
-
一个集合来求组合的话,就需要startIndex,例如:77.组合,216.组合总和III
-
多个集合取组合,各个集合之间相互不影响,那么就不用startIndex,例如:17.电话号码的字母组合
-
-
String 切割
-
切割过的地方,不能重复切割,和组合问题也是保持一致的
-
-
Permutations 排列
Combinations 组合 (不强调order)
- 选取一个a之后,在bcdef中再去选取第二个,选取b之后在cdef中在选组第三个.....
- 一个集合来求组合
- 77. Combinations, 39. Combination Sum, 40. Combination Sum ii, 216. Combination Sum iii (涉及去重)
- Number of solutions: = N!/(N-k)!k!
- Time Complexity: O(2^N * N) 组合问题其实就是一种子集的问题,所以组合问题最坏的情况,也不会超过子集问题的时间复杂度。
- 多个集合取组合
- 17. Letter Combinations of a Phone Number
- Time Complexity: O(4^N * N)
- Space Complexity: O(N)
String 切割
- 切割其实类似组合问题:切割一个a之后,在bcdef中再去切割第二段,切割b之后在cdef中在切割第三段.....
- 如何模拟那些切割线
- 切割问题中递归如何终止
- 在递归循环中如何截取子串
- 131. Palindrome Partitioning, 93. Restore IP Address
Subset 子集
- 子集也是一种组合问题,因为它的集合是无序的,子集{1,2} 和子集{2,1}是一样的
- 如果把子集问题、组合问题、分割问题都抽象为一棵树的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点
- 78. Subsets, 90. Subsets ii (涉及去重), 491. Increasing Subsequences(涉及去重)
- Number of solutions: 2^N Time Complexity: O(2^N * N) since each element could be absent or present.
Permutations 排列 (强调order)
- 不需要startIndex,每层都是从0开始搜索而不是startIndex
- 需要used数组记录temp里都放了哪些元素了
- 46. Permutations, 47. Permutations ii (涉及去重,对比subsets ii)
- Number of solutions: N! Time Complexity: O(N! * N)每一层节点为n,第二层每一个分支都延伸了n-1个分支,再往下又是n-2个分支,所以一直到叶子节点一共就是 n * n-1 * n-2 * ..... 1 = n!
Graph 拓展
- backtracking return boolean: 只需要找到一个行程,就是在树形结构中唯一的一条通向叶子节点的路线
- 332. Reconstruct Itinerary - 一般用dfs
N-Queen, Sudoku 棋盘
- N皇后问题抽象为一棵树,收集树的叶子节点(递归的深度就是棋盘的高度)
- Sudoku: 二维递归,每一层两个for loop
- 51. N Queens
- Time Complexity: O(N!) 不是O(N^N) 因为皇后之间不能见面,是N*N-1*N-2*... And it costs O(N^2) to build each valid solution
- Space Complexity: O(N^2) chessboard[][] is N*N. The recursion stack is O(N).
- 37. Sudoku Solver
- Time Complexity: Number of operations needed: (9!)^9 Let's consider one row, there are not more than 9 possibilities for the first number to put, not more than 9×8 for the second one, not more than 9×8×7 for the third one etc. In total that results in not more than 9! possibilities for a just one row, that means not more than (9!)×(9!)×...×(9!) = (9!)^9 operations in total.
- Space Complexity: O(9^2) 二维递归