目录
一、39. 组合总和
1.题目描述
给你一个 无重复元素 的整数数组 candidates
和一个目标整数 target
,找出 candidates
中可以使数字和为目标数 target
的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates
中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target
的不同组合数少于 150
个。
示例 1:
输入:candidates =[2,3,6,7],
target =7
输出:[[2,2,3],[7]] 解释: 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 7 也是一个候选, 7 = 7 。 仅有这两种组合。
示例 2:
输入: candidates = [2,3,5],
target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:
输入: candidates = [2],
target = 1
输出: []
2.解题思路
关键点1:如何确定终止条件?
- 当收集结果的path数组元素总和等于target的时候,就是收集结果的时候。
关键点2:如何确定单层递归逻辑?
- 首先我们将元素push到path中,然后更新一下sum总和
- 然后,在深度递归时候,不需要startIndex+1,(因为这里元素可以重复选取,所以直接从i开始就行)
- 最后,就是回溯操作。将path中的元素pop,更新sum。
3.代码实现
class Solution {
public:
vector<int> path;
vector<vector<int>> result;//结果集
void backtracking(vector<int>& candidates,int target,int sum,int startIndex){
if(sum > target)
return;//剪枝操作
//终止条件
if(sum == target){
//收集结果
result.push_back(path);
return;
}
//单层逻辑
for(int i = startIndex;i < candidates.size();i++){
path.push_back(candidates[i]);
sum += candidates[i];
backtracking(candidates,target,sum,i);
//回溯操作
sum -= candidates[i];
path.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
backtracking(candidates,target,0,0);
return result;
}
};
二、40. 组合总和 II
1.题目描述
给定一个候选人编号的集合 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
示例 1:
输入: candidates =[10,1,2,7,6,1,5]
, target =8
, 输出: [ [1,1,6], [1,2,5], [1,7], [2,6] ]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5, 输出: [ [1,2,2], [5] ]
2.解题思路
关键在于去重:先对所给数组排序,让相同元素在一起,没有必要让重复的元素都作为组合的开头元素。
- 例如所给数组:1、1、4、5、6、7;target = 8。符合条件的一组组合有 1 7 。如果以第一个1作为开头的组合,那么在以第二个1为开头时,就需要做去重操作。
3.代码实现
class Solution {
public:
vector<int> path;
vector<vector<int>> result;
void backtracking(vector<int>& candidates,int target,int sum,int startIndex){
//终止条件
if(sum > target)
return;
if(sum == target){
result.push_back(path);
return;
}
//单层逻辑
for(int i = startIndex;i < candidates.size();i++){
//树层去重
if(i > startIndex && candidates[i] == candidates[i - 1]){
continue;
}
path.push_back(candidates[i]);
sum += candidates[i];
backtracking(candidates,target,sum,i + 1);
//回溯操作
path.pop_back();
sum -= candidates[i];
}
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
//先对candidates排序,让相同元素靠在一起
sort(candidates.begin(),candidates.end());
backtracking(candidates,target,0,0);
return result;
}
};
三、131. 分割回文串
1.题目描述
给你一个字符串 s
,请你将 s
分割成一些子串,使每个子串都是 回文串 。返回 s
所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。
示例 1:
输入:s = "aab" 输出:[["a","a","b"],["aa","b"]]
示例 2:
输入:s = "a" 输出:[["a"]]
2.解题思路
分隔子串,其实和组合问题大同小异,例如 “aab”,取第一个a,相当于在a后面加了分割线,取第二个a,相当于在aa后面加了分割线。
关键点1:如何确定终止条件?
- 如果分隔线也就是startIndex到了题目所给字符串的末尾甚至更后面, 就停止,收集结果。
关键点2:单层逻辑怎么确定?
- 首先,需判断所分隔的子串是否为回文子串,如果是,就添加到path数组中,否则continue。这里的难点是,如何描述这个分割的子串:[startIndex,i]。
- 其次,进入深度递归,起始位置startIndex需要+1
- 最后,进入回溯操作,path.pop_back()
3.代码实现
class Solution {
public:
//编写函数,判断是否为回文数
bool isHuiWenChuan(string s,int start,int end){//遵循左闭右闭
int i = start,j = end;
for(;i < j;i++,j--){
if(s[i] != s[j])
return false;
}
//遍历结束,return true
return true;
}
vector<string> path;//存放符合条件的路径
vector<vector<string>> result;//结果集
void backtracking(string s,int startIndex){
//判断终止条件
if(startIndex >= s.size()){//起始点为字符串最后面了,没法继续切割了,就收集结果
//对路径是否符合题目要求的判断语句 写在单层循环里
result.push_back(path);
return;
}
//单层逻辑
for(int i = startIndex;i < s.size();i++){
//判断[startIndex,i]之间 是否为回文子串
if(isHuiWenChuan(s,startIndex,i)){
//如果是,就将子串存放到path中
string str = s.substr(startIndex,i - startIndex + 1);
path.push_back(str);
}
else{
//如果不是回文,就跳过
continue;
}
//开始深度递归
backtracking(s,i + 1);
//回溯操作
path.pop_back();
}
}
vector<vector<string>> partition(string s) {
backtracking(s,0);
return result;
}
};