回溯法
解决如下问题:
- 组合问题:N个数里面按一定规则找出k个数的集合
- 切割问题:一个字符串按一定规则有几种切割方式
- 子集问题:一个N个数的集合里有多少符合条件的子集
- 排列问题:N个数按一定规则全排列,有几种排列方式
- 棋盘问题:N皇后,解数独等等
组合问题
77.组合
注意剪枝操作可以举个例子计算循环边界值,加入item的时候new一个ArrayList
216.组合总和III
和77类似,不过可以把判断总和的部分放在叶子节点处理,不用放在循环里,在回溯的部分记得把总和也回溯一下,不要只回溯item。
17.电话号码的字母组合
一上来有点懵,其实把大问题分解就好了
数字与字母的对应可以用String[]解决(0和1下标可以用空字符串占位)
每轮递归遍历的是digits的每个数字,"23"里的2
每轮for循环遍历的是本轮数字对应字符串“abc“的a
39.组合总和
在挑选下一轮的数组起始位置时,可以从本位往后,而不是每一轮都从0开始
40.组合总和II
本轮的不可重复指的是在{1, 1, 2},3中不出现两个{1, 2},为了开销不可以在最后筛选去重,可以在处理之前进行判断,如果该数字相同的数字已经被遍历,则不再执行递归。
需要注意:for循环回溯是横向,递归是纵向,因此判断前面的数字已经被遍历的判断条件应该是used[i-1] == false
131.分割回文串
难点在于如何遍历分割,在for循环中,每次分割下标start~i对应的字符串就好
注意:substring含左不含右
93.复原IP地址
超级难!但是和131很像
递归结束条件应该是已经添加了三个分隔符,再检查一下剩余字符串的合法性
可以使用StringBuilder进行字符串更改
注意:合法性检查的时候先确保位数在3位内,不然直接parseInt会超限
子集问题
78.子集
组合问题是记录叶节点,所以改变list应该是在递归终止条件
而子集问题需要记录所有的节点,所以改变list应该是每一次函数调用时
90.子集II
和之前的类似,used数组判断重复
491.非递减子序列
需要注意不能用排序及used,可以用HashSet保存已经记录过的元素
HashSet<Integer> hs = new HashSet<>();
if (hs.contains(nums[i]))
continue;
排列问题
46.全排列
排列问题需要每轮用used记录已经使用的下标,选择没使用的
不需要start
47.全排列II
这里的used比较难理解
used[i - 1] == true,说明同⼀树⽀nums[i - 1]使⽤过
used[i - 1] == false,说明同⼀树层nums[i - 1]使⽤过
如果同⼀树层nums[i - 1]使⽤过则直接跳过
332.重新安排行程
注意字典序的排列,可以先把tickets按终点站字典序排列
Collections.sort(tickets, (a, b) -> a.get(1).compareTo(b.get(1)));
这个题可真难-_-
51.N皇后
我的天哪!我自己写出来了但是有一个逻辑判断因为我改了函数忘记从或改成并了
找了半个小时bug!痛苦
需要注意的语法:
// 二维数组不能直接fill
char[][] chars = new char[n][n];
for (char[] c : chars) {
Arrays.fill(c, '.');
}
// 数组转String
item.add(String.copyValueOf(c));
细心啊!!
37.解数独
需要注意这道题目需要三层for循环,遍历每一行每一列每一个数字可能性,递归的是每个数字可能性对应的解
由于找到一个解法就可以,所以需要return