题目:78.子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明: 解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
基本思想1:dfs
- 对不同长度的子集进行dfs
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> subsets(vector<int>& nums) {
int n = nums.size();
for(int i = 0; i <= n; ++i){
dfs(nums, i, vector<int>(), 0);
}
return res;
}
void dfs(vector<int> nums, int cnt, vector<int> cur, int pos){
if(cur.size() == cnt){
res.push_back(cur);
return;
}
for(int i = pos; i <= nums.size() - (cnt - cur.size()); ++i){
cur.push_back(nums[i]);
dfs(nums, cnt, cur, i + 1);
cur.pop_back();
}
}
};
递归
- 外层循环控制子集的长度,从长度为1到长度为nums.size()-1,长度为nums.size()直接加入到结果中
- 内层循环控制子集开始的位置,从第0个元素开始,直到nums.size()-len结束
- 递归来产生长度为len,以下标s开始的子集:
递归终止条件:下标不满足或者构成了长度为len的子集;
递归过程:将当前下标对应的元素存入当前结果cur中,并循环递归该元素的下一个元素。
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res = {{}};
if(nums.size() == 0)
return res;
vector<int> cur;
for(int len = 1; len < nums.size(); ++len){
for(int s = 0; s <= nums.size() - len; ++s)
combin(nums, s, len, res, cur);
}
res.push_back(nums);
return res;
}
private:
void combin(vector<int> &nums, int s, int len, vector<vector<int>> &res, vector<int> cur){
if(s >= nums.size())//递归终止条件
return;
cur.push_back(nums[s]);
if(cur.size() == len){
res.push_back(cur);
return;
}
for(int i = s + 1; i < nums.size(); ++i){
combin(nums, i, len, res, cur);
}
}
};
上述代码的改进,不考虑每次的起始元素,直接进行组合
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res = {{}};
if(nums.size() == 0)
return res;
vector<int> cur;
for(int len = 1; len < nums.size(); ++len){
//for(int s = 0; s <= nums.size() - len; ++s)
combin(nums, 0, len, res, cur);
}
res.push_back(nums);
return res;
}
private:
void combin(vector<int> &nums, int s, int len, vector<vector<int>> &res, vector<int> cur){
// if(s >= nums.size())//递归终止条件
// return;
if(cur.size() == len){
res.push_back(cur);
return;
}
for(int i = s; i < nums.size(); ++i){
cur.push_back(nums[i]);
combin(nums, i + 1, len, res, cur);
cur.pop_back();//回溯
}
}
};
上述代码的进一步改进,不考虑子集的长度,在递归的过程中,直接存入结果
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res = {};
if(nums.size() == 0)
return res;
vector<int> cur;
combin(nums, 0, res, cur);
return res;
}
private:
void combin(vector<int> &nums, int s, vector<vector<int>> &res, vector<int> cur){
res.push_back(cur);
for(int i = s; i < nums.size(); ++i){
cur.push_back(nums[i]);
combin(nums, i + 1, res, cur);
cur.pop_back();
}
}
};
基本思想2:迭代
- 遇到一个数就把该数加到所有的子集中,并把该子集添加到结果中
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res;
//遇到一个数就把该数加到所有的子集中
res.push_back({});
for(auto n : nums){
vector<vector<int>> cur = res;
for(auto c : cur){
c.push_back(n);
res.push_back(c);
}
}
return res;
}
};
基本思想3:位掩码
参考:位掩码
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
//位掩码来实现,元素在子集中用1来表示,不在子集中用0来表示
int n = 1 << nums.size();//表示有多少个子集
vector<vector<int>> res;
vector<int> cur;
for(int i = 0; i < n; ++i){//处理每一个子集
cur.clear();
for(int j = 0; j < nums.size(); ++j){//统计集合中有哪个元素在子集中
if((i >> j) & 1)
cur.push_back(nums[j]);
}
res.push_back(cur);
}
return res;
}
};