● 力扣题目链接
● 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
● candidates 中的数字可以无限制重复被选取。
● 数字可以重复选,因此我们每次递归从i开始,而不是i + 1
● 考虑剪枝,我们可以先把数组排序,然后一旦某时大于了tar,就直接break
● 时间复杂度:O(n * 2^n)
class Solution {
List<List<Integer>> res = new ArrayList();
LinkedList<Integer> path = new LinkedList();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates); // 先排序,方便后续剪枝
candidatesHelper(candidates, target, 0, 0);
return res;
}
private void candidatesHelper(int[] candidates, int target, int sum, int startIndex) {
if (sum >= target) {
if (sum == target) {
res.add(new ArrayList<>(path));
}
return;
}
for (int i = startIndex; i < candidates.length; i++) {
if (sum + candidates[i] > target) break; // 如果这个元素加上已经大于tar了,就不用再递归了
path.addFirst(candidates[i]);
sum += candidates[i];
candidatesHelper(candidates, target, sum, i);
sum -= candidates[i];
path.removeFirst();
}
}
}
● 力扣题目链接
● 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
● candidates 中的每个数字在每个组合中只能使用一次。
● 说明: 所有数字(包括目标数)都是正整数。解集不能包含重复的组合。
● 难点在于,数组中有重复的元素,还不能有重复的组合
● 我们要去重的是同一树层使用过的,同一树枝使用过的不用去重
○ 可以使用startIndex解决,遍历本层(循环)的时候,看一下不是第一个,且和前一个元素相同,那就结束本次循环
○ 或者我们使用used数组进行标记
■ 如果是同层的,递归回来会把used对应位置改回false
■ 如果是同枝的,不会改为false
● 时间复杂度:O(n * 2^n)
class Solution {
List<List<Integer>> res = new ArrayList();
LinkedList<Integer> path = new LinkedList();
int sum = 0;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
combinationSum2Helper(candidates, target, 0);
return res;
}
private void combinationSum2Helper(int[] candidates, int target, int startIndex) {
if (sum == target) {
res.add(new ArrayList<>(path));
return;
}
for (int i = startIndex; i < candidates.length; i++) {
// 去重同一树层使用过的元素
if (i > startIndex && candidates[i] == candidates[i - 1]) continue;
if (candidates[i] + sum > target) break;
sum += candidates[i];
path.addFirst(candidates[i]);
combinationSum2Helper(candidates, target, i + 1);
path.removeFirst();
sum -= candidates[i];
}
}
}
class Solution {
List<List<Integer>> res = new ArrayList();
LinkedList<Integer> path = new LinkedList();
int sum = 0;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
boolean[] used = new boolean[candidates.length];
Arrays.fill(used, false);
combinationSum2Helper(candidates, target, used, 0);
return res;
}
private void combinationSum2Helper(int[] candidates, int target, boolean[] used, int startIndex) {
if (sum == target) {
res.add(new ArrayList<>(path));
return;
}
for (int i = startIndex; i < candidates.length; i++) {
if (i > startIndex && candidates[i] == candidates[i - 1] && !used[i]) continue;
if (sum + candidates[i] > target) break;
sum += candidates[i];
path.addFirst(candidates[i]);
used[i] = true;
combinationSum2Helper(candidates, target, used, i + 1);
used[i] = false;
path.removeFirst();
sum -= candidates[i];
}
}
}
● 力扣题目链接
● 给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
● 返回 s 所有可能的分割方案。
● 示例: 输入: “aab” 输出: [ [“aa”,“b”], [“a”,“a”,“b”] ]
● startIndex是切割线
● 递归终止条件:切割线到了字符串末尾
● 单层递归逻辑:遍历字符串,一旦发现回文串,就加入path,然后回溯,继续向后看有没有回文串,回溯结束之后从path中弹出
class Solution {
List<List<String>> res = new ArrayList();
Deque<String> path = new ArrayDeque();
public List<List<String>> partition(String s) {
partitionHelper(s, 0);
return res;
}
private void partitionHelper(String s, int startIndex) {
// 遍历到末尾,找到了符合要求的,加入list
if (startIndex == s.length()) {
res.add(new ArrayList<>(path));
return;
}
for (int i = startIndex; i < s.length(); i++) {
if (isPalind(s, startIndex, i)) { // 如果这一段是回文串
path.addLast(s.substring(startIndex, i + 1)); // 把这一段加入path
partitionHelper(s, i + 1); // 继续递归
path.removeLast(); // 要回溯弹出,找下一段
}
}
}
// 判断字符串s在start到end这一段是不是回文串
private boolean isPalind(String s, int start, int end) {
while (start < end) {
if (s.charAt(start) != s.charAt(end)) return false;
start++;
end--;
}
return true;
}
}