78. 子集
题目描述
给你一个整数数组 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] <= 10nums中的所有元素 互不相同
解法
方法一:DFS(回溯)
思考
每个元素只有选与不选两种可能,子集共 \(2^n\) 个。\(n \le 10\),全部列出即可。
递归可以按元素下标展开这棵决策树:从下标 \(i\) 出发,先不选 \(nums[i]\),再选中并回溯弹出。当 \(i = n\) 时将当前路径拷入答案。题目保证元素互不相同,因此不会产生重复子集。
我们设计一个函数 \(dfs(i)\),表示从数组的第 \(i\) 个元素开始搜索所有子集。函数 \(dfs(i)\) 的执行逻辑如下:
- 如果 \(i=n\),表示当前已经搜索结束,将当前得到的子集 \(t\) 加入答案数组 \(ans\) 中,然后返回;
- 否则,我们可以选择不选择当前元素,直接执行 \(dfs(i+1)\);也可以选择当前元素,即把当前元素 \(nums[i]\) 加入子集 \(t\),然后执行 \(dfs(i+1)\),注意要在执行 \(dfs(i+1)\) 以后再将 \(nums[i]\) 从子集 \(t\) 中移除(回溯)。
在主函数中,我们调用 \(dfs(0)\),即从数组的第一个元素开始搜索所有子集。最后返回答案数组 \(ans\) 即可。
时间复杂度 \(O(n\times 2^n)\),空间复杂度 \(O(n)\)。其中 \(n\) 为数组的长度。一共有 \(2^n\) 个子集,每个子集需要 \(O(n)\) 的时间来构造。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
方法二:二进制枚举
思考
方法一依靠递归栈把「选 / 不选」展开,调用开销与递归深度均为 \(O(n)\)。
\(n\) 很小,也可以把每个子集对应成 \([0, 2^n)\) 中的一个掩码:第 \(i\) 位为 \(1\) 则收入 \(nums[i]\)。少一层函数调用,时间复杂度同为 \(O(n \times 2^n)\)。
我们也可以使用二进制枚举的方法得到所有的子集。
我们可以使用 \(2^n\) 个二进制数来表示 \(n\) 个元素的所有子集,对于当前二进制数 \(mask\),如果第 \(i\) 位为 \(1\),表示选择了第 \(i\) 个元素,否则表示不选择第 \(i\) 个元素。
时间复杂度 \(O(n\times 2^n)\),空间复杂度 \(O(n)\)。其中 \(n\) 为数组的长度。一共有 \(2^n\) 个子集,每个子集需要 \(O(n)\) 的时间来构造。
1 2 3 4 5 6 7 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |