题目来源:https://leetcode-cn.com/problems/subsets/
题目描述
给你一个整数数组nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums 中的所有元素 互不相同
题目大意
在一个数组中,返回所有的子集,包括空集合,本题如果用暴力法来做,会导致时空复杂度过大,且过程复杂,能直接想到的方法有迭代法和递归(或dfs)来做
递归(dfs)
在做递归的时候需要理解整体做题的思路,而不是顺着递归逐渐跳到下一层,维护一个midArrays数组,在递归的开始就直接将他放进结果ans里,然后添加下一个元素,然后再进到递归,跟着循环将当前数的子集全部push完,然后再pop_back再把最后一个元素给删了,再根据边界条件i∈[0,nums.size),最终ans中的所有元素就是当前数组的所有子集
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
int len = nums.size();
vector<int> temp;
vector<vector<int>> ans;
subsets(nums, 0, temp, ans);
return ans;
}
void subsets(vector<int> nums, int index, vector<int>& midArrays, vector<vector<int>>& ans){
ans.push_back(midArrays);
for(int i = index ; i < nums.size(); ++i){
midArrays.push_back(nums[i]);
subsets(nums, i + 1, midArrays, ans);
midArrays.pop_back();
}
}
};
复杂度分析
- 时间复杂度:O(n*2^n)。总共有2^n个子集,每个子集需要O(n)的时间构建
- 空间复杂度:O(n)。临时数组O(n),递归栈空间为O(n)
迭代
假设num[] = [1,2,3],可找到以下规律,由于子集数是2^n个,容易想到使用二进制数去表示所有子集包含原数组的哪些元素,当0/1序列为010时(1<<2)与mask位与,判断当前倒数第二位为1,那么此时临时数组temp应push(nums[1]),同理111则当前mask各个位为1,循环temp.push,最终再放入到ans中
0/1序列 | 子集 | 0/1序列对应的二进制数(mask) |
---|---|---|
000 | [] | 0 |
001 | [1] | 1 |
010 | [2] | 2 |
011 | [1,2] | 3 |
100 | [3] | 4 |
101 | [1,3] | 5 |
110 | [2,3] | 6 |
111 | [1,2,3] | 7 |
AC代码
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
int len = nums.size();
vector<int> temp;
vector<vector<int>> ans;
for(int mask = 0; mask < (1 << len) ; ++mask){
temp.clear();
for(int j = 0 ; j < len ; ++j){
if(mask & (1 << j)){
temp.push_back(nums[j]);
}
}
ans.push_back(temp);
}
return ans;
}
};
复杂度分析
- 时间复杂度:O(n*2^n)。双重循环,外层循环次数为2^n,里面为[0,n)的循环
- 空间复杂度:O(n)。临时数组O(n)