在 Python 中,求一个数组的所有子集可以通过多种方法实现。以下是几种高效且简洁的实现方式,包括迭代法、回溯法和位运算法,并附有详细解释和代码示例。
核心思想:
逐步构建子集,每次将当前元素添加到所有现有子集中,生成新的子集并合并到结果中。
Python 实现:
pythonCopy Code
示例和验证:
def subsets(nums):
res = [[]]
for num in nums:
res += [subset + [num] for subset in res]
return res
pythonCopy Code
print(subsets([1, 2, 3])) # 输出:[[], , , [1,2], , [1,3], [2,3], [1,2,3]]
复杂度分析:
核心思想:
通过递归枚举所有可能的子集,利用回溯撤销选择的状态,避免重复计算。
Python 实现:
pythonCopy Code
示例和验证:
pythonCopy Code
print(subsets_backtrack([1, 2, 3])) # 输出:[[], , [1,2], [1,2,3], [1,3], , [2,3], ]
复杂度分析:
核心思想:
每个子集对应一个二进制掩码,掩码的每一位表示是否选中对应位置的元素。
Python 实现:
pythonCopy Code
def subsets_bit(nums):
n = len(nums)
res = []
for mask in range(1 << n):
subset = [nums[i]
for i in range(n) :
if mask & (1 << i)]
res.append(subset)
return res
示例和验证:
pythonCopy Code
print(subsets_bit([1, 2, 3])) # 输出:[[], , , [1,2], , [1,3], [2,3], [1,2,3]]
复杂度分析:
方法 | 优点 | 缺点 |
---|---|---|
迭代法 | 代码简洁,无需递归 | 内存占用较高(需存储所有子集) |
回溯法 | 显式状态管理,适合复杂剪枝场景 | 递归可能栈溢出 |
位运算法 | 内存效率高 | 不适用于大数组(n > 20) |
推荐选择:
所有子集的数量应为 2ⁿ(n 为数组长度),可用以下公式验证结果正确性:
pythonCopy Code
nums = [1, 2, 3]
print(len(subsets(nums)) == 2 ** len(nums))
# 输出 True
题目:
1863. 找出所有子集的异或总和再求和 - 力扣(LeetCode)
class Solution:
def subsetXORSum(self, nums: List[int]) -> int:
res = []
self.backtrack(res, nums, 0, [])
# print(self.res)
count = 0
count_list = []
for it in res:
if len(it) > 0:
num = 0
for n in it:
num = num ^ n
count_list.append(num)
# print(count_list)
# print(res)
for i in count_list:
count += i
return count
return 0
def backtrack(self, res, nums, start, path):
res.append(path.copy()) # 拷贝
for i in range(start, len(nums)):
path.append(nums[i])
self.backtrack(res, nums, i + 1, path)
path.pop()