39. 组合总和
本题是 集合里元素可以用无数次,那么和组合问题的差别 其实仅在于 startIndex上的控制
题目链接/文章讲解:代码随想录
视频讲解:带你学透回溯算法-组合总和(对应「leetcode」力扣题目:39.组合总和)| 回溯法精讲!_哔哩哔哩_bilibili
//回溯三部曲
//可以取重复元素时i的处理
//剪枝,排序, 如果 sum + candidates[i] > target 就终止遍历
//注意这里要add(new LinkedList(path))
class Solution {
//新建List保存result
List<List<Integer>> result = new ArrayList<>();
//新建List保存每次得到的路径path
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
// 先进行排序
Arrays.sort(candidates);
backtracking(candidates, target, 0, 0);
return result;
}
//定义回溯方法
private void backtracking(int[] candidates, int target, int startIndex, int sum) {
//终止条件
if(sum == target) {
//!!!注意这里要add(new LinkedList(path))
//new LinkedList<>(path) will make a copy of path,
//which will make sure the elements be stored into result.
//Otherwise, after path.removeLast(),
//the elements in result will be removed.
result.add(new LinkedList(path));
return;
}
//for循环,单层搜索过程
//如果 sum + candidates[i] > target 就终止遍历
for(int i = startIndex; i <= candidates.length - 1; i++) {
if(sum + candidates[i] > target) {break;}
sum += candidates[i];
//处理节点
path.add(candidates[i]);
//递归;不用i+1了,表示可以重复读取当前的数
backtracking(candidates, target, i, sum);
//回溯
sum -= candidates[i];
path.removeLast();
}
}
}
40.组合总和II
本题开始涉及到一个问题了:去重。
题目链接/文章讲解:代码随想录
视频讲解:回溯算法中的去重,树层去重树枝去重,你弄清楚了没?| LeetCode:40.组合总和II_哔哩哔哩_bilibili
//回溯三部曲
//去重(树层 vs 树枝)
//排序,有利于剪枝和去重
class Solution {
//新建List保存result
List<List<Integer>> result = new ArrayList<>();
//新建List保存每次得到的路径path
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
//排序,有利于剪枝和去重
Arrays.sort(candidates);
backtracking(candidates, target, 0, 0);
return result;
}
//定义回溯方法
private void backtracking(int[] candidates, int target, int startIndex, int sum) {
//终止条件
if(sum == target) {
result.add(new LinkedList(path));
return;
}
//for循环,单层搜索过程
for(int i = startIndex; i < candidates.length && sum + candidates[i] <= target; i++ ) {
//去重
if(i > startIndex && candidates[i] == candidates[i - 1]) {
continue;
}
//处理节点,更新path
sum += candidates[i];
path.add(candidates[i]);
//递归
backtracking(candidates, target, i + 1, sum);
//回溯
sum -= candidates[i];
path.removeLast();
}
}
}
131.分割回文串
视频讲解:带你学透回溯算法-分割回文串(对应力扣题目:131.分割回文串)| 回溯法精讲!_哔哩哔哩_bilibili
//切割问题使用回溯
//用startIndex模拟那些切割线
//切割问题中递归如何终止: startIndex大于s的大小
//在递归循环中如何截取子串加入path: String str = s.substring(startIndex, i + 1)
class Solution {
//新建List保存result
List<List<String>> result = new ArrayList<>();
//新建List保存每次得到的路径path
LinkedList<String> path = new LinkedList<>();
public List<List<String>> partition(String s) {
backtracking(s, 0);
return result;
}
//定义回溯方法
private void backtracking(String s, int startIndex) {
//终止条件
//如果起始位置大于s的大小,说明找到了一组分割方案
if(startIndex >= s.length()) {
result.add(new LinkedList(path));
return;
}
//for循环,单层搜索过程
for(int i = startIndex; i < s.length(); i++) {
//如果是回文子串,则记录
if(isPalindrome(s, startIndex, i)) {
//获取[startIndex,i]在s中的子串
String str = s.substring(startIndex, i + 1);
path.add(str);
//不是则下一轮循环
} else {
continue;
}
//递归
backtracking(s, i + 1);
//回溯
path.removeLast();
}
}
//定义判断回文方法
private boolean isPalindrome(String s, int start, int end) {
for(int i = start, j = end; i < j; i++, j--) {
if(s.charAt(i) != s.charAt(j)){
return false;
}
}
return true;
}
}