回溯的基本概念
- 回溯法(backtracking)是暴力搜索法中的一种
- 回溯与递归密不可分
- 回溯法采用试错的思想:它尝试分步解决一个问题。在分步解决问题的过程中,当它通过尝试发现,现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。回溯法通常用最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种情况:
-
- 找到一个可能存在的正确的答案
-
- 在尝试了所有可能的分步方法后宣告该问题没有答案
回溯可以解决的问题
- 组合问题:无序
- 切割问题:切割字符串
- 子集问题
- 排列问题:有序
- 棋盘问题:N皇后、解数独
回溯解题思路
- 把所有问题都抽象成N叉树
-
- 树的宽度就是回溯处理的集合大小(横向for循环)
-
- 树的深度就是递归的深度(纵向递归)
一套固定格式搞定回溯
void backtracking(参数){
if(终止条件){
收集结果;
return;
}
for(集合元素){
处理节点;
递归;
回溯;
}
}
LeetCode 例题
组合问题:收集节点是树的叶子结点
- 77. 组合
- 39. 组合总和
- 40. 组合总和 II
-
- 区分树枝去重和树层去重,使用 used[] 数组
切割子串问题:子串为 [startIndex, i]
-
- 添加参数 pointNum ,利用节点个数判断终止
-
- 代码大致思路:在字符串 s 基础上添加 ’ . ’ 直到添加三个且最后一个子串也满足条件,将 s 放入结果集。
子集问题:收集节点是所有树节点,因此可以省略返回条件
- 78. 子集
- 90. 子集 II
-
- 注意数层去重中, 判断 used [ i - 1 ] 而不是 used[ i ]
- 491. 递增子序列
-
- 注意本题要求:在数组无序的情况下,进行数层去重
-
-
- 定义 set 数组在函数体中,达到数层去重效果
-
全排列问题:不用使用startIndex, 使用 used 数组
- 46. 全排列
- 47. 全排列 II
-
- 注意树层去重