回溯算法 Part02
组合总数
力扣题目链接
代码随想录链接
视频讲解
题目描述: 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
思路,本题的解法和part01的组合总和3的思路差别不大,其解树如下:
唯一的区别在于本题目之中的元素是可以重复选取的,因此,在递归传递参数的时候,我们应该传递i而不是i+1;
回溯法
具体代码如下:
class Solution {
int sum = 0;
List<Integer> path = new ArrayList<>();
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
backTracking(candidates,target,0);
return result;
}
private void backTracking(int[] candidates , int target , int startIndex){
// 剪枝操作
if(sum > target) return ;
if(startIndex > candidates.length) return ;
// 满足条件的情况存储
if(sum == target){
result.add(new ArrayList<>(path));
return ;
}
for(int i = startIndex ; i < candidates.length ; i++){
sum += candidates[i];
path.add(candidates[i]);
// 递归
backTracking(candidates,target,i);
// 回溯
sum -= candidates[i];
path.removeLast();
}
}
}
组合总数2
力扣题目链接
代码随想录链接
视频讲解
题目描述: 给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
思路:因为给的candidates 之中包含重复的元素,所以可能造成解集之中也包含同样的输出,但题目之中要求解集不能包含重复的组合。本题的解树为:
回溯法
思路:
class Solution {
LinkedList<Integer> path = new LinkedList<>();
List<List<Integer>> ans = new ArrayList<>();
boolean[] used;
int sum = 0;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
used = new boolean[candidates.length];
// 加标志数组,用来辅助判断同层节点是否已经遍历
Arrays.fill(used, false);
// 为了将重复的数字都放到一起,所以先进行排序
Arrays.sort(candidates);
backTracking(candidates, target, 0);
return ans;
}
private void backTracking(int[] candidates, int target, int startIndex) {
// if(sum > target) return ;
if (sum == target) {
ans.add(new ArrayList(path));
}
for (int i = startIndex; i < candidates.length; i++) {
if (sum + candidates[i] > target) {
break;
}
// 出现重复节点,同层的第一个节点已经被访问过,所以直接跳过
if (i > 0 && candidates[i] == candidates[i - 1] && !used[i - 1]) {
continue;
}
used[i] = true;
sum += candidates[i];
path.add(candidates[i]);
// 每个节点仅能选择一次,所以从下一位开始
backTracking(candidates, target, i + 1);
used[i] = false;
sum -= candidates[i];
path.removeLast();
}
}
}
回溯法(我的解决)
使用contains判断当前的结果path是否存在于result之中,不在则加入(会出现一些例子执行时间超出限制)
代码如下:
class Solution {
int sum = 0 ;
List<Integer> path = new ArrayList<>();
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
backTracking(candidates,target,0);
return result;
}
private void backTracking(int[] candidates , int target , int startIndex){
if(sum > target) return ;
if(sum == target && !result.contains(path)){
result.add(new ArrayList<>(path));
return ;
}
for(int i = startIndex ; i < candidates.length ; i++){
sum += candidates[i];
path.add(candidates[i]);
backTracking(candidates,target,i + 1);
sum -= candidates[i];
path.remove(path.size() - 1);
}
}
}
分割回文串 (待补充)
力扣题目链接
代码随想录链接
视频讲解
题目描述:
回溯法
代码如下:
class Solution {
//保持前几题一贯的格式, initialization
List<List<String>> res = new ArrayList<>();
List<String> cur = new ArrayList<>();
public List<List<String>> partition(String s) {
backtracking(s, 0, new StringBuilder());
return res;
}
private void backtracking(String s, int start, StringBuilder sb){
//因为是起始位置一个一个加的,所以结束时start一定等于s.length,因为进入backtracking时一定末尾也是回文,所以cur是满足条件的
if (start == s.length()){
//注意创建一个新的copy
res.add(new ArrayList<>(cur));
return;
}
//像前两题一样从前往后搜索,如果发现回文,进入backtracking,起始位置后移一位,循环结束照例移除cur的末位
for (int i = start; i < s.length(); i++){
sb.append(s.charAt(i));
if (check(sb)){
cur.add(sb.toString());
backtracking(s, i + 1, new StringBuilder());
cur.remove(cur.size() -1 );
}
}
}
//helper method, 检查是否是回文
private boolean check(StringBuilder sb){
for (int i = 0; i < sb.length()/ 2; i++){
if (sb.charAt(i) != sb.charAt(sb.length() - 1 - i)){return false;}
}
return true;
}
}