78. 子集
✅ 一、算法逻辑讲解(逐步通顺解释)
这段代码的目标是:给定一个不含重复元素的整数数组 nums
,返回其所有可能的子集(幂集)。
步骤解析:
-
1 << len(nums)
:-
这是 2^n 的简写方式,表示子集的总数。
-
因为一个长度为
n
的集合一共有2^n
个子集。
-
-
for i in range(1<<len(nums)):
-
i
从0
到2^n - 1
。 -
每个数字
i
的二进制表示就是一个子集的"选取方案",每一位表示该位置元素选不选。- 例如:
nums = [a, b, c]
,i = 5 = 0b101
表示选a
和c
(第0和第2位是1)。
- 例如:
-
-
[x for j, x in enumerate(nums) if i >> j & 1]
:-
枚举
nums
中的每个元素x
及其下标j
。 -
如果
i
的第j
位是1
,就说明这个元素被选入子集。 -
i >> j & 1
是判断第j
位是否为1的经典写法。
-
-
将生成的子集
subsets
加入答案列表ans
中。 -
返回所有子集的列表。
⭐ 二、核心思路
核心点是:用位运算枚举所有子集。
-
将每一个子集的选取用一个
n
位二进制数表示(0 表示不选,1 表示选)。 -
枚举
0
到2^n - 1
,每个整数的二进制形式唯一表示一个子集。 -
位运算用于高效判断哪些元素被选中。
这是一种非常高效、简洁的生成子集的方式,尤其适用于集合元素较少(如 n ≤ 20)的场景。
python
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
ans = []
for i in range(1<<len(nums)):
subsets = [x for j,x in enumerate(nums) if i>>j&1]
ans.append(subsets)
return ans
⏱ 三、时间复杂度分析
时间复杂度:O(n * 2^n)
-
一共有
2^n
个子集。 -
每个子集最多需要扫描
n
个元素来判断是否包含(通过i >> j & 1
判断)。 -
所以总的复杂度是:
O(n * 2^n)
。
比如
nums = [1,2,3]
,就需要计算2^3 = 8
个子集,每个最多判断 3 个位置。
💾 四、空间复杂度分析
空间复杂度:O(n * 2^n)
(输出结果空间)
-
每个子集可能长度为
n
,最多有2^n
个子集。 -
所以总的空间用于保存输出结果是
O(n * 2^n)
。
额外空间(不含输出):O(n)
-
每次生成一个子集使用一个列表(临时变量
subsets
),最多长度为n
。 -
所以辅助空间是
O(n)
。