给定一个整数数组 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
中的所有元素 互不相同
注意:本题与主站 78 题相同: https://leetcode-cn.com/problems/subsets/
方法一:迭代法实现子集枚举 思路与算法
记原序列中元素的总数为 n。原序列中的每个数字 a_i 的状态可能有两种,即「在子集中」和「不在子集中」。我们用 1 表示「在子集中」,0 表示不在子集中,那么每一个子集可以对应一个长度为 n 的 0/1 序列,第 i 位表示 a_i 是否在子集中。例如,n = 3 ,a = { 5, 2, 9 \ 时:
0/1 序列
子集
0/1 序列对应的二进制数
000
{ \
0
001
{ 9 \
1
010
{ 2 \
2
011
{ 2, 9 \
3
100
{ 5 \
4
101
{ 5, 9 \
5
110
{ 5, 2 \
6
111
{ 5, 2, 9 \
7
可以发现 0/1 序列对应的二进制数正好从 0 到 2^n - 1。我们可以枚举 mask} \in [0, 2^n - 1],mask 的二进制表示是一个 0/1 序列,我们可以按照这个 0/1 序列在原集合当中取数。当我们枚举完所有 2^n 个 mask,我们也就能构造出所有的子集。
代码
[sol1-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : vector<int > t; vector<vector<int >> ans; vector<vector<int >> subsets (vector<int >& nums) { int n = nums.size (); for (int mask = 0 ; mask < (1 << n); ++mask) { t.clear (); for (int i = 0 ; i < n; ++i) { if (mask & (1 << i)) { t.push_back (nums[i]); } } ans.push_back (t); } return ans; } };
[sol1-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution { List<Integer> t = new ArrayList <Integer>(); List<List<Integer>> ans = new ArrayList <List<Integer>>(); public List<List<Integer>> subsets (int [] nums) { int n = nums.length; for (int mask = 0 ; mask < (1 << n); ++mask) { t.clear(); for (int i = 0 ; i < n; ++i) { if ((mask & (1 << i)) != 0 ) { t.add(nums[i]); } } ans.add(new ArrayList <Integer>(t)); } return ans; } }
[sol1-Golang] 1 2 3 4 5 6 7 8 9 10 11 12 13 func subsets (nums []int ) (ans [][]int ) { n := len (nums) for mask := 0 ; mask < 1 <<n; mask++ { set := []int {} for i, v := range nums { if mask>>i&1 > 0 { set = append (set, v) } } ans = append (ans, append ([]int (nil ), set...)) } return }
[sol1-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var subsets = function (nums ) { const ans = []; const n = nums.length ; for (let mask = 0 ; mask < (1 << n); ++mask) { const t = []; for (let i = 0 ; i < n; ++i) { if (mask & (1 << i)) { t.push (nums[i]); } } ans.push (t); } return ans; };
[sol1-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int ** subsets (int * nums, int numsSize, int * returnSize, int ** returnColumnSizes) { int ** ans = malloc (sizeof (int *) * (1 << numsSize)); *returnColumnSizes = malloc (sizeof (int ) * (1 << numsSize)); *returnSize = 1 << numsSize; int t[numsSize]; for (int mask = 0 ; mask < (1 << numsSize); ++mask) { int tSize = 0 ; for (int i = 0 ; i < numsSize; ++i) { if (mask & (1 << i)) { t[tSize++] = nums[i]; } } int * tmp = malloc (sizeof (int ) * tSize); memcpy (tmp, t, sizeof (int ) * tSize); (*returnColumnSizes)[mask] = tSize; ans[mask] = tmp; } return ans; }
复杂度分析
方法二:递归法实现子集枚举 思路与算法
我们也可以用递归来实现子集枚举。
假设我们需要找到一个长度为 n 的序列 a 的所有子序列,代码框架是这样的:
[demo1-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 vector<int > t; void dfs (int cur, int n) { if (cur == n) { return ; } t.push_back (cur); dfs (cur + 1 , n, k); t.pop_back (); dfs (cur + 1 , n, k); }
上面的代码中,dfs}(\textit{cur}, n) 参数表示当前位置是 cur,原序列总长度为 n。原序列的每个位置在答案序列中的状态有被选中和不被选中两种,我们用 t 数组存放已经被选出的数字。在进入 dfs}(\textit{cur}, n) 之前 [0, \textit{cur} - 1] 位置的状态是确定的,而 [\textit{cur}, n - 1] 内位置的状态是不确定的,dfs}(\textit{cur}, n) 需要确定 cur 位置的状态,然后求解子问题 {\text{dfs}(cur + 1}, n)。对于 cur 位置,我们需要考虑 a[\textit{cur}] 取或者不取,如果取,我们需要把 a[\textit{cur}] 放入一个临时的答案数组中(即上面代码中的 t),再执行 {\text{dfs}(cur + 1}, n),执行结束后需要对 t 进行回溯;如果不取,则直接执行 {\text{dfs}(cur + 1}, n)。在整个递归调用的过程中,cur 是从小到大递增的,当 cur 增加到 n 的时候,记录答案并终止递归。可以看出二进制枚举的时间复杂度是 O(2 ^ n)。
代码
[sol2-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : vector<int > t; vector<vector<int >> ans; void dfs (int cur, vector<int >& nums) { if (cur == nums.size ()) { ans.push_back (t); return ; } t.push_back (nums[cur]); dfs (cur + 1 , nums); t.pop_back (); dfs (cur + 1 , nums); } vector<vector<int >> subsets (vector<int >& nums) { dfs (0 , nums); return ans; } };
[sol2-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { List<Integer> t = new ArrayList <Integer>(); List<List<Integer>> ans = new ArrayList <List<Integer>>(); public List<List<Integer>> subsets (int [] nums) { dfs(0 , nums); return ans; } public void dfs (int cur, int [] nums) { if (cur == nums.length) { ans.add(new ArrayList <Integer>(t)); return ; } t.add(nums[cur]); dfs(cur + 1 , nums); t.remove(t.size() - 1 ); dfs(cur + 1 , nums); } }
[sol2-Golang] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func subsets (nums []int ) (ans [][]int ) { set := []int {} var dfs func (int ) dfs = func (cur int ) { if cur == len (nums) { ans = append (ans, append ([]int (nil ), set...)) return } set = append (set, nums[cur]) dfs(cur + 1 ) set = set[:len (set)-1 ] dfs(cur + 1 ) } dfs(0 ) return }
[sol2-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var subsets = function (nums ) { const t = []; const ans = []; const n = nums.length ; const dfs = (cur ) => { if (cur === nums.length ) { ans.push (t.slice ()); return ; } t.push (nums[cur]); dfs (cur + 1 , nums); t.pop (t.length - 1 ); dfs (cur + 1 , nums); } dfs (0 , nums); return ans; };
[sol2-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 int ** ans;int * ansColSize;int ansSize;int * t;int tSize;void dfs (int cur, int * nums, int numsSize) { if (cur == numsSize) { int * tmp = malloc (sizeof (int ) * tSize); memcpy (tmp, t, sizeof (int ) * tSize); ansColSize[ansSize] = tSize; ans[ansSize++] = tmp; return ; } t[tSize++] = nums[cur]; dfs(cur + 1 , nums, numsSize); tSize--; dfs(cur + 1 , nums, numsSize); } int ** subsets (int * nums, int numsSize, int * returnSize, int ** returnColumnSizes) { ans = malloc (sizeof (int *) * (1 << numsSize)); ansColSize = malloc (sizeof (int ) * (1 << numsSize)); t = malloc (sizeof (int ) * numsSize); *returnSize = 1 << numsSize; ansSize = tSize = 0 ; dfs(0 , nums, numsSize); *returnColumnSizes = ansColSize; return ans; }
复杂度分析