class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> list = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
backTracking(candidates, target, 0, 0);
return ans;
}
public void backTracking(int[] candidates, int target,int sum, int startIndex) {
if (sum == target) {
ans.add(new ArrayList<>(list));
return;
}
for (int i = startIndex; i < candidates.length; i++) {
if (sum + candidates[i] > target) continue;
list.add(candidates[i]);
int newSum = sum + candidates[i];
backTracking(candidates, target, newSum, i);
list.removeLast();
}
}
}
40.组合总和II
本题开始涉及到一个问题了:去重。
我们要去重的是同一树层上的“使用过”,同一树枝上的都是一个组合里的元素,不用去重。
题目链接/文章讲解:代码随想录
视频讲解:回溯算法中的去重,树层去重树枝去重,你弄清楚了没?| LeetCode:40.组合总和II_哔哩哔哩_bilibili
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> list = new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
backTracking(candidates, target, 0, 0);
return ans;
}
public void backTracking(int[] candidates, int target, int sum, int startIndex) {
if (sum == target) {
ans.add(new ArrayList<>(list));
return;
}
for (int i = startIndex; i < candidates.length; i++) {
if (sum + candidates[i] > target) continue;
if (i > startIndex && candidates[i] == candidates[i - 1]) continue;
list.add(candidates[i]);
backTracking(candidates, target, sum + candidates[i], i + 1);
list.removeLast();
}
}
}
131.分割回文串
视频讲解:带你学透回溯算法-分割回文串(对应力扣题目:131.分割回文串)| 回溯法精讲!_哔哩哔哩_bilibili
class Solution {
List<List<String>> ans = new ArrayList<>();
List<String> list = new ArrayList<>();
public List<List<String>> partition(String s) {
backTracking(s, 0);
return ans;
}
public void backTracking(String s, int startIndex) {
if (startIndex == s.length()) {
ans.add(new ArrayList<>(list));
return;
}
for (int i = startIndex; i < s.length(); i++) {
if (isPalindrome(s, startIndex, i)) {
String str = s.substring(startIndex, i + 1); //substring 左闭右开截取
list.add(str);
} else {
continue;
}
backTracking(s, i + 1);
list.removeLast();
}
}
private boolean isPalindrome(String s, int startIndex, int end) {
for (int i = startIndex, j = end; i < j; i++, j--) {
if (s.charAt(i) != s.charAt(j)) {
return false;
}
}
return true;
}
}
模拟这段代码的执行流程,以字符串 "aab"
为例:
-
开始调用
partition("aab")
方法:- 这个方法调用
backTracking("aab", 0)
。
- 这个方法调用
-
进入
backTracking
方法,此时startIndex = 0
。- 循环开始,
i
从 0 到 2(字符串"aab"
的长度减 1)。
- 循环开始,
-
第一轮循环:
i = 0
。- 调用
isPalindrome("aab", 0, 0)
,检查"a"
是否是回文。是的,因此继续。 s.substring(0, 0 + 1)
→ 截取"a"
,并添加到list
中,现在list = ["a"]
。- 递归调用
backTracking("aab", 1)
。
- 调用
-
进入
backTracking
的下一层递归,此时startIndex = 1
。- 循环开始,
i
从 1 到 2。 - 第一轮循环:
i = 1
。- 调用
isPalindrome("aab", 1, 1)
,检查"a"
是否是回文。是的,因此继续。 s.substring(1, 1 + 1)
→ 截取"a"
,并添加到list
中,现在list = ["a", "a"]
。- 递归调用
backTracking("aab", 2)
。
- 调用
- 循环开始,
-
进入更深层的递归,此时
startIndex = 2
。- 循环开始,
i
从 2 到 2。 - 第一轮循环:
i = 2
。- 调用
isPalindrome("aab", 2, 2)
,检查"b"
是否是回文。是的,因此继续。 s.substring(2, 2 + 1)
→ 截取"b"
,并添加到list
中,现在list = ["a", "a", "b"]
。- 递归调用
backTracking("aab", 3)
。
- 调用
- 循环开始,
-
递归基准情况满足(
startIndex == s.length()
),将["a", "a", "b"]
添加到ans
中。- 回溯:移除
list
中最后一个元素,list
变回["a", "a"]
。
- 回溯:移除
-
回溯到
startIndex = 1
的层次。- 回溯后的循环继续,
i
变为 2。 - 检查
"aab"
的子串"ab"
(s.substring(1, 2 + 1)
)是否是回文。不是,因此跳过。 - 循环结束,执行
list.removeLast()
,移除"a"
,list
变回["a"]
。
- 回溯后的循环继续,
-
回到最初的
backTracking
调用,此时startIndex = 0
。- 循环继续,
i
变为 1。 - 检查
"aab"
的子串"aa"
(s.substring(0, 1 + 1)
)是否是回文。是的,因此继续。 - 添加
"aa"
到list
中,现在list = ["a", "aa"]
。 - 递归调用
backTracking("aab", 2)
。
- 循环继续,
-
再次进入递归,此时
startIndex = 2
。- 循环开始,
i
从 2 到 2。 - 检查
"b"
是否是回文。是的,因此继续。 - 添加
"b"
到list
中,现在list = ["a", "aa", "b"]
。 - 递归调用
backTracking("aab", 3)
。
- 循环开始,
-
基准情况满足,将
["a", "aa", "b"]
添加到ans
中。- 回溯:移除
list
中最后一个元素,list
变回["a", "aa"]
。
- 回溯:移除
-
回溯到
startIndex = 0
的层次。- 回溯后的循环结束,执行
list.removeLast()
,移除"aa"
,list
变回["a"]
。
- 回溯后的循环结束,执行
-
所有循环结束,所有可能的分割方式已经被添加到
ans
中。
最终,ans
包含了所有可能的分割方式:[["a", "a", "b"], ["aa", "b"]]
。通过这种方式,代码逐步探索了不同的子串分割方式,每次都检查是否为回文,并适时地进行回溯以探索不同的路径。