主要是回溯算法,对于回溯算法,首先脑子里需要大概的模拟,然后就可以套模板了,个人认为目前比较难的是参数的确认以及适当剪枝。
一、回溯是什么(代码随想录网站):带你学透回溯算法(理论篇)| 回溯法精讲!_哔哩哔哩_bilibili
重点抽象为树形结构,然后就好理解了。
二、回溯算法可以解决什么问题:
- 组合问题:N个数里面按一定规则找出k个数的集合
- 排列问题:N个数按一定规则全排列,有几种排列方式
- 切割问题:一个字符串按一定规则有几种切割方式
- 子集问题:一个N个数的集合里有多少符合条件的子集
- 棋盘问题:N皇后,解数独等等
三、模板:回溯三部曲是什么?
以上所有内容均来自代码随想录网站:代码随想录
1.回溯的理论
三部曲怎么走?
回溯法抽象为树形结构后,其遍历过程就是:for循环横向遍历,递归纵向遍历,回溯不断调整结果集。
- 回溯函数模板返回值以及参数
返回值void
一般是先写逻辑,然后需要什么参数,就填什么参数 - 回溯函数终止条件
if (终止条件) { 存放结果; return; }
- 回溯搜索的遍历过程
回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成的树的深度。for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) { 处理节点; backtracking(路径,选择列表); // 递归 回溯,撤销处理结果 }
2.组合问题:代码随想录
力扣链接:. - 力扣(LeetCode)
个人理解都在代码注释中(包含剪枝):
/**
* 组合
*
* https://leetcode.cn/problems/combinations/
*/
public class _22_1Combine1 {
static List<List<Integer>> result = new ArrayList<>();
static LinkedList<Integer> path = new LinkedList<>();
public static List<List<Integer>> combine(int n, int k) {
backTracking(n,k,1);
return result;
}
public static void backTracking(int n,int k,int startIndex){
if (path.size() == k){
result.add(new ArrayList<>(path));
return;
}
for (int i = startIndex; i<= n-k+path.size()+1;i++){ //这是剪枝过的回溯
path.add(i); //1.注意要添加的元素
backTracking(n, k, i+1); //2.回溯
path.removeLast(); //3.注意不断调整结果集
}
}
public static void main(String[] args) {
int n =4;
int k =2;
System.out.println(combine(n,k));
}
}
3.组合总和:代码随想录
力扣链接:. - 力扣(LeetCode)
/**
* 组合总和III
*
* https://leetcode.cn/problems/combination-sum-iii/
*/
public class _22_3Combine3 {
static List<List<Integer>> result = new ArrayList<>();
static LinkedList<Integer> path = new LinkedList<>();
public static List<List<Integer>> combinationSum3(int k, int n) {
backTracking(n, k, 1, 0); //还是回溯的模板,看看里面的逻辑有什么不同
return result;
}
public static void backTracking(int targetNum, int k, int startIndex, int sum) {
if (sum > targetNum) { //1.增加了判断,还是回溯的终止条件
return;
}
if (path.size() == k) { //控制回溯的深度
result.add(new ArrayList<>(path));
return;
}
for (int i = 1; i < 9 - k + path.size() + 1; i++) { //还是可以剪枝的
path.add(i);
sum += i; //2.多了个sum+=i的操作,因为要找组合的和
backTracking(targetNum, k, i + 1, sum); //还是回溯
path.removeLast(); //不断调整参数,模板核心没变
sum -= i; //调整参数记得同时调整sum
}
}
}
4.电话号码的字母组合:代码随想录
力扣链接:. - 力扣(LeetCode)
注意用数组实现数字和字母的映射关系操作
注意numIndex表示的含义
public class _22_4PhoneNumberLetterCombination {
List<String> result = new ArrayList<>();
public List<String> letterCombinations(String digits) {
if (digits == null || digits.length() == 0) {
return result;
}
//初始化数组,对应数字的字符
String[] numStrings = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
backTracking(digits, numStrings, 0);
return result;
}
StringBuilder temp = new StringBuilder(); //StringBuilder操作更灵活些
public void backTracking(String digits, String[] numStrings, int numIndex) { //numIndex就是当前回溯的深度,用来遍历digits
if (numIndex == digits.length()) {
result.add(temp.toString()); //断点1,可以看看怎么执行的
return;
}
String str = numStrings[digits.charAt(numIndex) - '0'];
for (int i = 0; i < str.length(); i++) {
temp.append(str.charAt(i));
//递归,下一层处理
backTracking(digits, numStrings, numIndex + 1); //断点2,可以看看怎么执行的
//尝试别的组合,调整参数
temp.deleteCharAt(temp.length() - 1);
}
}
public static void main(String[] args) {
String input = "23";
_22_4PhoneNumberLetterCombination phoneNumberLetterCombination = new _22_4PhoneNumberLetterCombination();
System.out.println(phoneNumberLetterCombination.letterCombinations(input));
}
}
上面几个题都是回溯的模板应用,属于第一级难度,多练习,无他,唯手熟尔(本人菜鸡一枚,脑子还不灵光,只能多记录了),以下截图源于代码随想录