基础
回溯的本质是穷举,一般适用于组合、切割、子集、排列、棋盘等问题。
回溯问题抽象为树形结构,for循环-树层,递归-树枝
回溯法模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
77.组合
- 参数:
n, k, startIndex(防止重复的数字出现)
- 递归结束条件,单条路径收集数字组合
size==k
,结果集收集,终止 - 单条递归逻辑:防止重复出现
startInddex+1
216. 组合总和 III
- 终止条件:
sum==targetSum
,结果集收集 - 用sum统计每个数字之后,作为参数传递到递归函数
17. 电话号码的字母组合
- 首先给定,每个按键对应的字符串,string数组列举出来
- string数组用来收集结果,string s收集单条路径的结果
- 这里不需要startIndex来控制重复出现的次数,但是需要一个index来控制输入字符串中每个数字的索引,先通过index找出输入字符串digits对应的按键,根据对应的按键找到对应的字符串
- 开始for循环遍历,边界是按键字符串的大小,递归时,
index+1
39. 组合总和
- 题目要求可以重复读取,startIndex不用+1操作
- 用sum统计数组中的数字之和,当
sum==targe
t时,存入结果集
40. 组合总和 II
- 题目要求不能重复,used数组用来解决树层和树枝上的去重
used[i - 1] == true,说明同一树枝candidates[i - 1]使用过, used[i - 1] == false,说明同一树层candidates[i - 1]使用过
- 回溯之前,需要进行判断,同一树层使用过的元素跳过
- 回溯前:
sum+
,单条路径收集,used=true
- 回溯后:
sum-
,单条路径pop,used=false
131.分割回文串
- 终止条件:
startIndex>=s.size()
,存入结果集 - for循环遍历,回溯之前,判断是否回文串,截取字符串存入单条路径收集,不是回文,跳过
- 回溯参数,起始索引+1
- 判断回文:双指针指向字符串起始位置和结束位置,同时遍历,如果索引处的值不相等就不是回文,否则是回文
93.复原IP地址
- IP地址以逗点数量作为分割结束的条件,一个完整的IP地址有3个逗点
- 回溯参数,字符串,startIndex,记录逗点数量
- 单层逻辑,先判断子串是否合法, 如果合法,在字符之后加逗点,开始回溯,进行下一个字符的判断
- 判断字符串是否合法,参数左右区间,以及字符串,遇到0开头的的数字不合法,遇到非数字字符不合法,如果大于255了不合法
78.子集
- 每条路径上都收集结果,并不是叶子节点才收集,回溯一开始就收集结果集
90.子集II
- 树层去重,树枝可以重复,used数组树层去重
- 如果
nums[i-1]==nums[i]&&used[i-1]==false
,同一树层使用过,continue
,跳过 - 单条路径收集,used置为true,开始回溯,索引i+1
491.递增子序列
- 题目要求不重复,
unordered_set
树层去重 - 当前元素小于单条路径的最后一个元素或者uset集合没有当前重复的元素,就跳过
46.全排列
- 叶子节点收集结果集,used数组去重,
used[i] == true
,树层去重
47.全排列 II
- used数组去重,
i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false
树层去重
51. N皇后
- 棋盘是否合法:不能同行,不能同列,不能斜对角线
- 回溯参数:棋盘大小n,行数row,字符串
- 终止条件:如果
row==n
,说明到达最大行,收集结果集 - 单层逻辑:不用同行进行比较,在for循环遍历时已经同行比较了
chessboard[row][col] = 'Q'; // 放置皇后;chessboard[row][col] = '.'; // 回溯,撤销皇后