组合问题:不讲究顺序,按照一定规则从N个中找出k个数
模板
//组合问题模板
vector<vector<int>>result;
vector<int>path;
vector<vector<int>>main(vector<int>&nums) {
sort(nums.begin(), nums.end())
dfs();
return result;
}
void dfs(){
if(终止条件){
r
return;
}
for(int i=start; i<nums.size(); i++){ //循环动作空间
剪枝() break;
path.push_back(i);
dfs();
path.pop_back();
}
}
无序组合类型总结
(1) 数组元素不重复 不可重复取 start = i+1
(2) 数组元素不重复 可重复取 start = i
(3) 数组元素重复 不可重复取 start = i+1 同一树层剪枝
if( i>start && candidates[i]==candidates[i-1]) continue;
216. 组合总和 III
1.动作范围 1-9 取k个 和为n
2. 组合问题不重复 i = start +1
void dfs(int k, int target, int start){
if(target == 0 && path.size() == k){
result.push_back(path);
return;
}
for(int i=start; i<=9; i++){
if(target - i <0 || path.size()==k) break;
path.push_back(i);
dfs(k, target-i, i+1);
path.pop_back();
}
}
39. 组合总和
4. 组合 无序 为了保证不重复 使用start来表示起始位置
5. 结束条件 sum==target
6. 剪枝 sum > target
7. 无限制重复选取 start为 i 也就是从当前元素开始取
class Solution {
public:
vector<vector<int>>result;
vector<int>path;
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end());
dfs(candidates, target, 0);
return result;
}
void dfs(vector<int>& candidates, int target, int start){
cout<<start << " " << candidates[start] <<" "<<target<<endl;
if(target < 0) return;
if(target == 0){
result.push_back(path);
return;
}
for(int i=start; i<candidates.size(); i++){
path.push_back(candidates[i]);
dfs(candidates, target-candidates[i], i);
path.pop_back();
}
}
};
40. 组合总和 II
1.组合问题,用start保证顺序
2. 集合中每个元素只用选用一次 start = i+1;
3. 集合中有重复元素, 去重
(1) if( i>0 && candidates[i]==candidates[i-1]) continue;
这样会导致树枝层面的去重 (1,1,2)这样的结果丢失
(2) if( i>0 && candidates[i]==candidates[i-1] && uesed[i-1] == false) continue;
定义一个used数组来记录元素使用情况, 如果当前元素跟跟前一个元素相同 前一个元素没有用,这个肯定是重复的。因为所有的组合情况前面那个元素已经做了
(3) if( i>start && candidates[i]==candidates[i-1]) continue;
for循环就是同一个树层的动作选择 ,跳过同一树层使用过的元素,
for(int i=start; i<candidates.size(); i++){
if( i>start && candidates[i]==candidates[i-1]) continue;
path.push_back(candidates[i]);
dfs(candidates, target-candidates[i], i+1);
path.pop_back();
}
- 顺序不同的序列被视作不同的组合。去掉start for循环从下标0开始
- 重复计算太多 递归剪枝(记忆化搜索),否则会超时
int dfs(vector<int>& candidates, int target){
if(mp.find(target)!=mp.end()){return mp[target];}
if(target == 0){
mp[target] = 1;// 当taget=0时 方法个数为1
return 1;
}
int cur = 0;
for(int i=0; i<candidates.size(); i++){
if(target - candidates[i] < 0) break; //放在外面 需要遍历整个数组嘛
//break比较省事
cur += dfs(candidates, target-candidates[i]);
}
mp[target] = cur; //存在即返回 不存在就算完之后赋值
return mp[target];
}
17. 电话号码的字母组合
for循环是循环该树层的动作空间
class Solution {
public:
unordered_map<char,string> mp={{'2',"abc"},{'3',"def"},{'4',"ghi"},{'5',"jkl"}, {'6',"mno"},{'7',"pqrs"},{'8',"tuv"},{'9',"wxyz"}};
vector<string>result;
string path;
vector<string> letterCombinations(string digits) {
if(digits.size()==0) return {};
dfs(digits, 0);
return result;
}
void dfs(string digits, int index){
if(index > digits.size()) return;
if(index == digits.size()){
result.push_back(path);
return;
}
string ss = mp[digits[index]];
for(int i=0; i<ss.size(); i++){ //每一个数字代表的动作空间
path.push_back(ss[i]);
dfs(digits, index+1); //回溯下一个数字
path.pop_back();
}
}
};