给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
思路分析: 这种排列、组合问题,比较常用的就是回溯法(深度优先搜索)。
就是使用一个中间变量保存一个中间结果,每次从未使用的元素中挑出一个元素放入中间结果,当中间结果符合解的条件,那么就找到了一个解。然后把中间结果的上一步操作撤回,继续选择其他元素…
具体代码实现
class Solution {
public:
int n;
int k;//对象属性,作用类似全局变量
vector<vector<int>> result;
vector<bool> numFlag;//numFlag[i]用于标记i是被使用
void combineDFS(vector<int> &tempRes, int steps){
if (steps == k){
result.push_back(tempRes);
}
else {
for (int i = 1; i <= n; ++i){
if (numFlag[i] == false){//这个数字未使用
tempRes.push_back(i);//放入尾端
numFlag[i] = true;//标记使用
combineDFS(tempRes, steps + 1);//继续下一步搜索
tempRes.pop_back();//使用完后将尾部pop
numFlag[i] = false;//标记未使用
}
}
}
}
vector<vector<int>> combine(int n, int k) {
result.clear();
numFlag = vector<bool>(n + 1, false);//初始化标记容器
if (n < k){
return result;
}
this->n = n;
this->k = k;
vector<int> tempRes;
combineDFS(tempRes, 0);//开始dfs
return result;
}
};
发现出现重复的解,因为 先选择A再选择B 与 先选择B再选择A 是同一个组合,但是代码对于这种重复的组合解没有进行处理。
优化思路:维持选入tempRes的元素保持递增的特性。在dfs函数中添加一个参数,用于记录上一步操作使用的元素值,下一步选取操作只选取未使用、且比上一步元素值大的元素。(这样就不出现{2,3}和{3,2}这种重复解,因为递增特性2必定比3先选入,不可能出现先选3的情况。)
class Solution {
public:
int n;
int k;//对象属性,作用类似全局变量
vector<vector<int>> result;
vector<bool> numFlag;//numFlag[i]用于标记i是被使用
//steps用于标记当前已找到的元素数,last用于标记上一步操作选取的元素值
void combineDFS(vector<int> &tempRes, int last, int steps){
if (steps == k){
result.push_back(tempRes);
}
else {
//只能选取比上一步操作大的元素,才能保证tempRes元素递增
for (int i = last + 1; i <= n; ++i){
if (numFlag[i] == false){//这个数字未使用
tempRes.push_back(i);//放入尾端
numFlag[i] = true;//标记使用
combineDFS(tempRes, i, steps + 1);//继续下一步搜索
tempRes.pop_back();//使用完后将尾部pop
numFlag[i] = false;//标记未使用
}
}
}
}
vector<vector<int>> combine(int n, int k) {
result.clear();
numFlag = vector<bool>(n + 1, false);//初始化标记容器
if (n < k){
return result;
}
this->n = n;
this->k = k;
vector<int> tempRes;
combineDFS(tempRes, 0, 0);//开始dfs
return result;
}
};