递归
每帧递归栈只需要选当前元素与不选当前元素即可,记得回溯时要恢复现场了。
class Solution {
private:
vector<int> tmp;
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res;
dfs(0,nums,res);
return res;
}
void dfs(int x, vector<int> &nums, vector<vector<int>> &res) {
if(x == nums.size()) {
res.emplace_back(tmp);
return;
}
dfs(x+1,nums,res);
tmp.emplace_back(nums[x]);
dfs(x+1,nums,res);
tmp.pop_back();
}
};
迭代实现
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
int len = nums.size();
int n = 1<<len;
vector<vector<int>> res;
vector<int> tmp;
for(int i = 0; i < n; i++) {
tmp.clear();
for(int j = 0; j < len; j++) {
if(i&(1<<j)) {
tmp.emplace_back(nums[j]);
}
}
res.emplace_back(tmp);
}
return res;
}
};
最容易想到的方法获得所有组合方式,从中筛选出我们所需的,时间复杂度 O ( 2 n ) O(2^n) O(2n) 。
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> res;
vector<int> tmp;
dfs(1,n,k,tmp,res);
return res;
}
void dfs(int x, int n, int k, vector<int> &tmp, vector<vector<int>> &res) {
if(tmp.size() == k) {
res.emplace_back(tmp);
return;
} else if(x == n+1) return;
dfs(x+1,n,k,tmp,res);
tmp.emplace_back(x);
dfs(x+1,n,k,tmp,res);
tmp.pop_back();
}
};
递归剪枝优化
如果剩下的数不够选就直接退出递归,时间复杂度就等于
O
(
C
k
n
)
O(C^n_{k})
O(Ckn)
class Solution {
private:
public:
vector<vector<int>> res;
vector<int> tmp;
int n,k;
vector<vector<int>> combine(int n, int k) {
this->n = n;
this->k = k;
dfs(1,1);
return res;
}
void dfs(int select, int start) {
if(n-start+1 < k-select+1) return; //剪枝,如果剩下的数不够选就直接退出
if(select == k+1) {
res.emplace_back(tmp);
return;
}
for(int i = start; i <= n; i++) {
tmp.emplace_back(i);
dfs(select+1, i+1);
tmp.pop_back();
}
}
};
消除递归,大神做法(巨难理解!)
解释
class Solution {
public:
vector<int> temp;
vector<vector<int>> ans;
vector<vector<int>> combine(int n, int k) {
// 初始化
// 将 temp 中 [0, k - 1] 每个位置 i 设置为 i + 1,即 [0, k - 1] 存 [1, k]
// 末尾加一位 n + 1 作为哨兵
for (int i = 1; i <= k; ++i) {
temp.push_back(i);
}
temp.push_back(n + 1);
int j = 0;
while (j < k) {
ans.emplace_back(temp.begin(), temp.begin() + k);
j = 0;
// 寻找第一个 temp[j] + 1 != temp[j + 1] 的位置 t
// 我们需要把 [0, t - 1] 区间内的每个位置重置成 [1, t]
while (j < k && temp[j] + 1 == temp[j + 1]) {
temp[j] = j + 1;
++j;
}
// j 是第一个 temp[j] + 1 != temp[j + 1] 的位置
++temp[j];
}
return ans;
}
};
二叉树结构
class Solution {
public:
void dfs(vector<int>& candidates, int target, vector<vector<int>>& ans, vector<int>& combine, int idx) {
if (idx == candidates.size()) {
return;
}
if (target == 0) {
ans.emplace_back(combine);
return;
}
// 直接跳过
dfs(candidates, target, ans, combine, idx + 1);
// 选择当前数
if (target - candidates[idx] >= 0) {
combine.emplace_back(candidates[idx]);
dfs(candidates, target - candidates[idx], ans, combine, idx);
combine.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> ans;
vector<int> combine;
dfs(candidates, target, ans, combine, 0);
return ans;
}
};
多叉树结构(更省空间,似乎是少了很多函数栈帧)
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> path;
dfs(candidates, path, res, target, 0);
return res;
}
void dfs(vector<int> &candidates, vector<int> &path, vector<vector<int>> &res, int target, int idx){
if (target == 0){
res.push_back(path);
return;
}
for (int i = idx; i < candidates.size(); i++){
if(target-candidates[i] >= 0) {
path.push_back(candidates[i]);
dfs(candidates, path, res, target-candidates[i], i);
path.pop_back();
}
}
}
};