题目
给你一个整数数组 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中的所有元素 互不相同
思路
解法一: 递归枚举子集
迭代枚举子集
初始化res=[]是任意数组的子集,在遍历nums时,对res的所有结果中加入nums[i],然后再将所有加入了nums[i]的数组加入res。迭代遍历完成。
代码一:迭代枚举子集
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
# 方法一: 迭代枚举子集
res = [[]]
for i in range(len(nums)):
subsets = []
for ret in res:
# 在这里进行浅拷贝,否则会影响res内部的元素值
subset = ret[:]
subset.append(nums[i])
subsets.append(subset)
# 将当前结果加入到res中
res.extend(subsets)
return res
递归实现枚举
其实和迭代思路一样,只是用递归的写法做出来,核心就是递归到i位置的结果等于i-1位置的结果加上i-1的每个结果都加入nums[i]。
f(i) = f(i-1) + (f(i-1), nums[i])
不难发现,递归的退出条件为i==0,结果为[]。
代码二:递归获取子集
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
# 方法二:递归
def recursive(cur, nums):
if cur == 0:
res.append([])
return res
prev_res = recursive(cur-1, nums)
# 最后一位是nums[i-1]的子集
subsets = []
for ret in prev_res:
# 对ret进行浅拷贝,不影响ret的原值
subset = ret[:]
subset.append(nums[cur-1])
subsets.append(subset)
# 将res的结果更新
res.extend(subsets)
return res
res = []
return recursive(len(nums), nums)
解法二: 回溯
回溯法:一般是每一步确定方案的一个值,然后继续寻找下一步解决方案。如果当前方案不是正确解决方案,回溯法通过修改上一步的值,继续寻找解决方案。
通过查找不同的子集长度来查找一个类型的值,判断剩余的长度是否为0,再决定将结果是否存入res。
- 对所有长度 0<=k<=n
- 对开始位置0<=start<=n寻找长度为k的子集
- 如果当前子集长度为k,则当前子集构建完成,将其加入输出结果中
- 将nums[start]加入自己
- 以当前子集为基础在nums[start+1, n-1]位置寻找长度为k-1的子集
- 包含nums[start]的结果已经完全找到,将nums[start]移除,寻找下一个可能的子集
代码三:回溯
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
# 回溯
def backtrace(start, k, cur, nums):
if k == 0: #说明已经满足了k的长度
ret = cur[:] #一定要进行浅拷贝,不然无法保存这次结果
res.append(ret)
return
for i in range(start, n):
cur.append(nums[i]) #将当前位置加入结果中
backtrace(i+1, k-1, cur, nums) #寻找i+1位置开始,k-1长度的子串
cur.pop() #当前nums[i]的结果已经查找完毕,弹出进行下一次i+1位置的查找
return res
res = []
n = len(nums)
for i in range(n+1):
backtrace(0, i, [], nums) # 对每个不同长度的子集进行查找结果
return res
解法三: 二进制位图获取子集
题目有个条件,nums的数字无重复值,所以它的所有组合应该是C(n,0) + C(n, 1) + ... + C(n,n) = 2^n 所以可以根据,n位2进制数,通过设置0,1来进行判断该位是否取值,2^n到2^(n+1)左闭右开区间内的所有数字,可以覆盖到这样情况。
代码四:二进制位图来获取子集
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
res = []
n = len(nums)
for i in range(2**n, 2**(n+1)):
bitmask = bin(i)[3:] # 不能用lstrip("0b1"),因为碰到第4位为0的时会被省略
ret = []
for j in range(n):
if bitmask[j] == "1":
ret.append(nums[j])
res.append(ret)
return res