Problem
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
Example
For example,
If n = 4 and k = 2, a solution is:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
Algorithm
整理一下题意:给定两个整数n和k,要求返回1~n中所有包含k个数的组合(注意不是排列)。
与全排列题目相似,只是要求组合而非排列,所以对于全排列的代码需要进行修改。具体来说,就是当某个数被选中后,进入下一次的生成过程,从生成过程返回后,全排列代码中需要重新把该数标记为未选,而本题由于是求组合,所以不需要改变该数的状态,即保持已选状态。这样做是为了保证不出现重复的组合。
代码如下。
//Time limit exceeded
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<bool> pick(n+1,false);
vector<int> temp;
vector<vector<int>> result;
generate(pick,temp,result,n,k);
return result;
}
void generate(vector<bool> pick,vector<int> temp,vector<vector<int>>&result,int n,int k){
if(temp.size()==k){
result.push_back(temp);
return;
}
for(int i=1;i<=n;i++){
if(pick[i]==false){
temp.push_back(i);
pick[i]=true;
generate(pick,temp,result,n,k);
temp.pop_back();
}
}
}
};
提交后发现超时,Time limitt exceeded。查看测试样例,发现只有最后一组样例没能通过。代码是正确,但是却超时了,那么需要进行优化。
注意到组合与排列的不同点:选过的数可以不再考虑。那么全排列中设置的用来检查每个数选中情况的pick向量可以简化掉。只要对于每次生成范围进行缩减,排除上一次已选的数即可。此外,通过改变k值来限定取数的个数。
由于每次都缩小了取数的范围,所以递归的规模一直在减小,于是时间复杂度得以降低,重新提交代码,通过。
代码如下。
//用时519ms
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<int> temp;
vector<vector<int>> result;
generate(temp,result,n,k);
return result;
}
void generate(vector<int> temp,vector<vector<int>>&result,int n,int k){
if(k==0){
result.push_back(temp);
return;
}
for(int i=n;i>=1;i--){
if(k>0){
temp.push_back(i);
generate(temp,result,i-1,k-1);
temp.pop_back();
}
}
}
};