题目如下:原题链接
法一 依次考虑每个元素
利用递归,依次考虑每个元素的取舍,这样一共能得到2n个状态,递归深度最深为n层。这里有一个小优化,定义一个全局的vector<int> 用来存放递归取的元素集合,每当取一个元素,则push_back,当递归回溯的时候就pop_back,这样递归的时候就无需传入额外的vector<int>数组了。
class Solution {
public:
vector<vector<int>> ans;
vector<int> tmp;
vector<vector<int>> subsets(vector<int>& nums) {
//从第0层开始递归
dfs(0,nums);
return ans;
}
//传入nums的引用,加快了递归的过程,同时减小空间开销
void dfs(int n,vector<int> &nums){
//递归达到边界
if(n==nums.size()){
ans.push_back(tmp);
return;
}
//不取这个元素
dfs(n+1,nums);
//取这个元素
tmp.push_back(nums[n]);
dfs(n+1,nums);
//递归回溯,将这个元素从集合中删去
tmp.pop_back();
}
};
法二 位运算求解
假设我们从 [1,2]中生成子集,那么可以有四个子集,分别是空集(0b00)、{2}(0b01),{1}(0b10),{1,2}(0b11),注意到原数组只有两个元素,上述的四个子集可以看成是 0 - 3 的二进制这四种状态,对应位的1代表取那个数,而0代表不取那个数,此时一共可以得到2n个子集,符合我们的认知。可以利用此性质来进行求解,这样可以使用递推来求解而非递归。
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
//n为2^(nums元素个数)
int n=1<<(nums.size());
vector<vector<int>> ans;
//每个i对应一种状态
for(int i=0;i<n;i++){
vector<int> tmp;
//针对每一位来判断是否取该位数字
for(int j=0;j<nums.size();j++)
//为1,则取该数字
if(i&(1<<j))
tmp.push_back(nums[j]);
//将这种状态加入ans
ans.push_back(tmp);
}
return ans;
}
};