39. 组合总和
参考文章:代码随想录
参考视频:带你学透回溯算法-组合总和(对应「leetcode」力扣题目:39.组合总和)| 回溯法精讲!_哔哩哔哩_bilibili
解题思路:
本题搜索的过程抽象成树形结构如下:
注意图中叶子节点的返回条件,因为本题没有组合数量要求,仅仅是总和的限制,所以递归没有层数的限制,只要选取的元素总和超过target,就返回!
确定参数,需要参数startIndex来确定for循环的起始位置,传进来的数组candidates,目标值target,已经总和sum初始值为0。
确定终止条件,当sum值等于或者超过target值的时候结束递归。
确定单层递归逻辑,用for循环遍历,初始值为startIndex,将sum+=candidates[i]后,将该值传到路径数组path中,然后开始递归。
注意回溯,sum值和数组path都需要进行回溯操作。
剪枝,当for循环遍历的时候sum已经加起来大于target值了,那么直接break退出循环即可。
public class Leetcode39 {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
track(candidates, target, 0, 0);
return res;
}
public void track(int[] candidates, int target, int sum, int startIndex) {
if (sum == target) {
res.add(new ArrayList<>(path));
return;
}
for (int i = startIndex; i < candidates.length; i++) {
if (sum + candidates[i] > target) break;
path.add(candidates[i]);
sum += candidates[i];
track(candidates, target, sum, i);
sum -= candidates[i];
path.remove(path.size() - 1);
}
}
}
组合总和II
参考文章:代码随想录
参考视频:回溯算法中的去重,树层去重树枝去重,你弄清楚了没?| LeetCode:40.组合总和II_哔哩哔哩_bilibili
解题思路:
因为不能使用到同一个元素,所以需要一个标记数组used来判断该元素是否被用过,在进入递归函数前需要将数组进行排序,通过该数组来避免两个相同值的不同元素在同一层递归逻辑中重复使用,注意在candidates[ i ] == candidates[ i - 1 ] 还需要额外判断candidates[ i - 1 ] 是否为false,如果不加这个条件的话结果一定会少,因为这影响到了向下递归时候出现的重复值,没有将该值添加到path路径中。
递归函数参数,和上题一样,额外加了一个用来标记数字是否被使用过的数组used。
递归终止条件,当sum值等于target或大于target的时候结束。
确定单层递归逻辑,for循环开始递归candidates数组,当出现重复值时跳过该循环,将值加入路径中,sum+=candidates【i】,进行下一层递归。
注意回溯。
public class Leetcode40 {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
boolean[] used = new boolean[candidates.length];
track(candidates, target, 0, 0, used);
return res;
}
public void track(int[] candidates, int target, int sum, int startIndex, boolean[] used) {
if (sum > target) return;
if (sum == target) {
res.add(new ArrayList<>(path));
return;
}
for (int i = startIndex; i < candidates.length; i++) {
if (i > 0 && candidates[i] == candidates[i - 1] && !used[i - 1]) {
continue;
}
path.add(candidates[i]);
sum += candidates[i];
used[i] = true;
track(candidates, target, sum, i + 1, used);
sum -= candidates[i];
used[i] = false;
path.remove(path.size() - 1);
}
}
}
131.分割回文串
参考文章:代码随想录
参考视频:带你学透回溯算法-分割回文串(对应力扣题目:131.分割回文串)| 回溯法精讲!_哔哩哔哩_bilibili
解题思路:
递归用来纵向遍历,for循环用来横向遍历,切割线(就是图中的红线)切割到字符串的结尾位置,说明找到了一个切割方法。
确定递归参数,传入字符串s,for循环初始位置startIndex,同时也是作为切割符对字符串进行切割。
终止条件,当切割符等于字符串的长度时,将路径值添加到结果集中,return。
确定单层递归逻辑,用for循环遍历字符串,判断i到startIndex之间的字符串是否是回文串,若是将该字符串添加到路径集中,若不是则continue进入到下层循环。进行递归。
public class Leetcode131 {
List<List<String>> res = new ArrayList<>();
List<String> path = new ArrayList<>();
public List<List<String>> partition(String s) {
track(s, 0);
return res;
}
public void track(String s, int startIndex) {
if (startIndex == s.length()) {
res.add(new ArrayList<>(path));
}
for (int i = startIndex; i < s.length(); i++) {
if (isPalindrome(s, startIndex, i)) {
path.add(s.substring(startIndex, i + 1));
} else {
continue;
}
track(s, i + 1);
path.remove(path.size() - 1);
}
}
public boolean isPalindrome(String s, int left, int right) {
while (left < right) {
if (s.charAt(left) != s.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
}