combinations【子集+回溯法】
题目描述:Given two integers n and k, return all possible combinations of k numbers out of 1 … n…For example,
If n = 4 and k = 2, a solution is:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
回溯法的思想:尝试+回溯+remove
class Solution {
public:
/*在1-n里面找到k个数的组合*/
vector<vector<int> > combine(int n, int k) {
vector<int> list;//存放一种组合
vector<vector<int> > results; //存放所以组合
backtracking(1,n,k,list,results);
return results;
}
//回溯算法-尝试+回溯+remove
/*从start到end里面寻找k个数字*/
void backtracking(int start,int end,int k,vector<int> &list,vector<vector<int> > &results){
if(k==0){
results.push_back(list);
return;
}else{
for(int i=start;i<=end;i++){
list.push_back(i); //尝试
backtracking(i+1,end,k-1,list,results); //回溯
list.pop_back();
}
}
}
};
Permutations【全排列-不排序-回溯法-vector】
题目描述:Given a collection of numbers, return all possible permutations.For example,
[1,2,3]have the following permutations:
[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2], and[3,2,1].
思路:交换当前位置和开头
class Solution {
public:
//回溯法
//找出Nums里的元素的所有排列组合(nums是collection:可能存在重复元素)
/*重要:交换-回溯-交换*/
vector<vector<int> > permute(vector<int> &num) {
vector<vector<int> > results;
backtracking(0,num,results);
return results;
}
//*l:记录当前取得是当前下标为l的元素
void backtracking(int l,vector<int> &num,vector<vector<int> > &results){
if(l==num.size()-1){
results.push_back(num);
return;
}else{
for(int i=l;i<=num.size()-1;i++){
swap(num[i],num[l]);
backtracking(l+1,num,results);
swap(num[l],num[i]);
}
}
}
};
Permutations-II【全排列-不排序-回溯法-set】
题目描述:Given a collection of numbers that might contain duplicates, return all possible unique permutations.For example,
[1,1,2]have the following unique permutations:
[1,1,2],[1,2,1], and[2,1,1].
思路:同 上面的pertatins相比,用set替换vector,由于set.insert的特征;注意外层为set,内层vector不可以用vectorname.size()
class Solution {
public:
//回溯法
//Based on permutation:用set而不是采用vector!!
vector<vector<int> > permuteUnique(vector<int> &num) {
int n=num.size();
set<vector<int> > res;
backtracking(0,n,num,res);
vector<vector<int> > results;
results.assign(res.begin(),res.end());
return results;
}
//*l:记录当前取得是当前下标为l的元素
void backtracking(int l,int n,vector<int> &num,set<vector<int> > &res){
if(l==n){
res.insert(num);
return;
}else{
for(int i=l;i<n;i++){
swap(num[i],num[l]);
backtracking(l+1,n,num,res);
swap(num[l],num[i]);
}
}
}
};
Combination Sum【子集-排序-回溯法-vector】
题目描述:Given a set of candidate numbers ( C ) and a target number ( T ), find all unique combinations in C where the candidate numbers sums to T .The same repeated number may be chosen from C unlimited number of times.Note:
All numbers (including target) will be positive integers.
Elements in a combination (a 1, a 2, … , a k) must be in non-descending order. (ie, a 1 ≤ a 2 ≤ … ≤ a k).The solution set must not contain duplicate combinations.
For example, given candidate set2,3,6,7and target7,
A solution set is:
[7]
[2, 2, 3]
回溯算法:尝试+回溯+remove;注意利用candidates均为正数控制循环次数( c a n d i d a t e s [ i ] < = t a r g e candidates[i]<=targe candidates[i]<=targe),利用start控制重复组合!
相比于permutation:有target,故先对数组排序
class Solution {
public:
vector<vector<int> > combinationSum(vector<int> &candidates, int target) {
//先将candidates排序
sort(candidates.begin(),candidates.end());
vector<int> list;//记录一种组合
vector<vector<int> > results;//记录返回结果
backtracking(candidates,0,target,list,results);
return results;
}
/*回溯算法--尝试+回溯+remove控制*/
void backtracking(vector<int> &candidates,int start,int target,vector<int> &list,vector<vector<int> > &results){
if(target==0){
results.push_back(list);
return;
}else{
/*错误:for(int i=0;i<candidates.size();i++)*/
/*注意:利用start控制重复*/
/*利用candidates均为正数优化!!candidates[i]<=target!!*/
for(int i=start;i<candidates.size()&& candidates[i]<=target;i++){
list.push_back(candidates[i]); //尝试
backtracking(candidates,i,target-candidates[i],list,results);
list.pop_back();
}
}
}
};
Combination Sum-II【子集-排序-回溯法-set】
Given a collection of candidate numbers ( C ) and a target number ( T ), find all unique combinations in C where the candidate numbers sums to T .Each number in C may only be used once in the combination.Note:
All numbers (including target) will be positive integers.Elements in a combination (a 1, a 2, … , a k) must be in non-descending order. (ie, a 1 ≤ a 2 ≤ … ≤ a k).The solution set must not contain duplicate combinations.For example, given candidate set10,1,2,7,6,1,5and target8,
A solution set is:
[1, 7]
[1, 2, 5]
[2, 6]
[1, 1, 6]
**回溯算法:尝试+回溯+remove;
同上题的相似点:
- 注意利用candidates均为正数控制循环次数( c a n d i d a t e s [ i ] < = t a r g e candidates[i]<=targe candidates[i]<=targe),利用start控制重复组合;
不同于上题:
- (1)clollection包括list和set,set是不允许重复的,但由于list是可以重复,所以collection可能重复。所以上题的candidates排完序本身就没有重复元素**
- (2)此题为了避免结果重复,引入set记录结果,因为插入一个已经存在的元素对容器没有影响
相比于permutation:有target,故先对数组排序
class Solution {
public:
vector<vector<int> > combinationSum2(vector<int> &num, int target) {
//先将num排序
sort(num.begin(),num.end());
vector<int> list;//记录一种组合
//利用set记录结果,保证没有重复
set<vector<int> > setOfResults;//记录返回结果
vector<vector<int> > results;
backtracking(num,0,target,list,setOfResults);
//vector赋值assign:将set中的元素赋值给vector
results.assign(setOfResults.begin(),setOfResults.end());
return results;
}
/*回溯算法--尝试+回溯+remove控制*/
void backtracking(vector<int> &candidates,int start,int target,vector<int> &list,set<vector<int> > &results){
if(target==0){
//set包含不重复的关键字,因此插入一个已经存在的元素对容器没有影响
results.insert(list);
return;
}else{
/*错误:for(int i=0;i<candidates.size();i++)*/
/*注意:利用start控制重复*/
/*利用candidates均为正数优化!!candidates[i]<=target!!*/
for(int i=start;i<candidates.size()&& candidates[i]<=target;i++){
list.push_back(candidates[i]); //尝试
backtracking(candidates,i+1,target-candidates[i],list,results);
list.pop_back();
}
}
}
};
Subset【子集-排序-回溯法-vector】
题目描述:Given a set of distinct integers, S, return all possible subsets.Note:Elements in a subset must be in non-descending order.The solution set must not contain duplicate subsets.
For example,
If S =[1,2,3], a solution is:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
思路:for循环实现递归+getList函数实现回溯
解释:
(1)排序的原因是为了保持子集里的元素非减
(2)回溯法:尝试+回溯+remove
(3)vector:尽管结果要求不含重复元素,但因为S中本身是不含重复元素的,故直接用vector即可
class Solution {
public:
//注意:用vector指针访问元素时,也可以用size()求长度
vector<vector<int> > subsets(vector<int> &S) {
sort(S.begin(),S.end());//对s排序:为了nondescending
vector<int> list;
vector<vector<int> > results;//记录返回结果
for(int i=0;i<=S.size();i++)
getList(0,i,S,list,results);
//step1:在num中取i个元素即可
return results;
}
//在有序s中取n个元素,list是n个元素的集合,allList是所有list的集合
void getList(int start,int n,vector<int> s,vector<int> &list,vector<vector<int> > &results){
if(n==0){
results.push_back(list);
return;
}else{
for(int i=start;i<s.size();i++){
list.push_back(s[i]);//尝试
getList(i+1,n-1,s,list,results);
list.pop_back();
}
}
}
};
Subset-II【子集-排序-回溯法-set】
题目描述:Given a collection of integers that might contain duplicates, S, return all possible subsets.Note:Elements in a subset must be in non-descending order.
The solution set must not contain duplicate subsets.For example,
If S =[1,2,2], a solution is:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
思路:for循环实现递归+getList函数实现回溯
解释:
(1)排序的原因是为了保持子集里的元素非减
(2)回溯法:尝试+回溯+remove
(3)set:结果要求不含重复元素,而且S中本身是可能含重复元素,故用set
class Solution {
public:
//注意:使用set
vector<vector<int> > subsetsWithDup(vector<int> &S) {
sort(S.begin(),S.end());//对s排序:为了nondescending
vector<int> list;
set<vector<int> > results;//记录返回结果
vector<vector<int> > res;
for(int i=0;i<=S.size();i++)
getList(0,i,S,list,results);
res.assign(results.begin(),results.end());
//step1:在num中取i个元素即可
return res;
}
//在有序s中取n个元素,list是n个元素的集合,allList是所有list的集合
void getList(int start,int n,vector<int> s,vector<int> &list,set<vector<int> > &results){
if(n==0){
results.insert(list);
return;
}else{
for(int i=start;i<s.size();i++){
list.push_back(s[i]);//尝试
getList(i+1,n-1,s,list,results);
list.pop_back();
}
}
}
};
permutation-sequence【非回溯】
题目描述:The set[1,2,3,…,n]contains a total of n! unique permutations【排列组合】.By listing and labeling all of the permutations in order,We get the following sequence (ie, for n = 3):
“123”
“132”
“213”
“231”
“312”
“321”
Given n and k, return the k th permutation sequence.Note: Given n will be between 1 and 9 inclusive.
class Solution {
public:
string getPermutation(int n, int k) {
string res;
string num = "123456789";
vector<int> f(n, 1);
for (int i = 1; i < n; ++i) f[i] = f[i - 1] * i;
--k;
for (int i = n; i >= 1; --i) {
int j = k / f[i - 1];
k %= f[i - 1];
res.push_back(num[j]);
num.erase(j, 1); //不能用vector.erase(begin+index)因为begin会改变
}
return res;
}
};