39. 组合总和
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/combination-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> temp = new LinkedList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
backtracking(candidates,target,0,0);
return res;
}
private void backtracking(int [] candidates, int target, int currSum, int startIndex){
if(currSum>target){
return;
}
if(currSum==target){
res.add(new ArrayList<>(temp));
return;
}
for(int i = startIndex;i<candidates.length;i++){
currSum+=candidates[i];
temp.add(candidates[i]);
// 注意可以无限制重复选取
backtracking(candidates,target,currSum,i);
temp.removeLast();
currSum-=candidates[i];
}
}
}
剪枝
需要先排序,否则可能遗漏 比如 [3,7,1] target = 4 如果看到 3+7 >4 直接返回就会遗漏正确答案
当看到本层: currSum+当前元素candidates[i] > target 时就可以直接返回
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> temp = new LinkedList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
// 剪枝需要排序 否则会漏掉
Arrays.sort(candidates);
backtracking(candidates,target,0,0);
return res;
}
private void backtracking(int [] candidates, int target, int currSum, int startIndex){
if(currSum>target){
return;
}
if(currSum==target){
res.add(new ArrayList<>(temp));
return;
}
for(int i = startIndex;i<candidates.length;i++){
// 剪枝
if(currSum+candidates[i]>target) break;
currSum+=candidates[i];
temp.add(candidates[i]);
// 注意可以无限制重复选取
backtracking(candidates,target,currSum,i);
temp.removeLast();
currSum-=candidates[i];
}
}
}
40. 组合总和||
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/combination-sum-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> temp = new LinkedList<>();
// 去重
boolean [] used;
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,0);
return res;
}
private void backtracking(int [] candidates, int target, int currSum, int startIndex){
if(currSum==target){
res.add(new ArrayList<>(temp));
}
for(int i=startIndex;i<candidates.length;i++){
// 剪枝
if(currSum + candidates[i]>target){
break;
}
// 同层:如果和前一个节点一样且前一个节点已经被访问过 直接跳过
if(i>0&&candidates[i]==candidates[i-1]&&!used[i-1]){
continue;
}
used[i] = true;
currSum+=candidates[i];
temp.add(candidates[i]);
backtracking(candidates,target,currSum,i+1);
used[i] = false;
currSum-=candidates[i];
temp.removeLast();
}
}
}
注意 用一个标签数组进行去重
used[i-1] = true, 表示同一树枝 candidates[i] 使用过
used[i-1] = false,表示同一树层 candidates[i] 使用过 {因为used[i-1]==false才能表示,当前 candidates[i] 是从 candidates[i-1] 回溯而来的
131. 分割回文串
给你一个字符串
s
,请你将s
分割成一些子串,使每个子串都是 回文串 。返回s
所有可能的分割方案。回文串 是正着读和反着读都一样的字符串。
class Solution {
List<List<String>> res = new ArrayList<>();
LinkedList<String> temp = new LinkedList<>();
public List<List<String>> partition(String s) {
backtracking(s,0);
return res;
}
private void backtracking(String s, int startIndex){
// 如果起始位置大于s的大小说明找到了分割方案
if(startIndex>=s.length()){
res.add(new ArrayList<>(temp));
return;
}
for(int i=startIndex;i<s.length();i++){
// 如果[startIndex,i]是回文串则添加到temp中
if(isPalindrome(s,startIndex,i)){
String str = s.substring(startIndex, i + 1);
temp.add(str);
}else{
// 如果[startIndex,i]不是回文串则跳过当前循环进入下一个循环
continue;
}
// 起始位置后移保证不重复
backtracking(s,i+1);
// 回溯
temp.removeLast();
}
}
private boolean isPalindrome(String s, int startIndex, int endIndex){
while(startIndex<=endIndex){
if(s.charAt(startIndex)!=s.charAt(endIndex)){
return false;
}
startIndex++;
endIndex--;
}
return true;
}
}