78. 子集
Posted 炫云云
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了78. 子集相关的知识,希望对你有一定的参考价值。
78. 子集
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
输入:nums = [0]
输出:[[],[0]]
迭代法
记原序列中元素的总数为 n 0 n_{0} n0 原序列中的每个数字 a i a_{i} ai 的状态可能有两种,即「在子集中」和「不在 子集中」。我们用 1 表示「在子集中」, 0 表示不在子集中,那么每一个子集可以对应一个长度为 n n n 的 0 / 1 0 / 1 0/1 序列,第 i i i 位表示 a i a_{i} ai 是否在子集中。例如, n = 3 , a = { 5 , 2 , 9 } n=3, a=\\{5,2,9\\} n=3,a={5,2,9} 时:
可以发现 0 / 1 0 / 1 0/1 序列对应的二进制数正好从 0 到 2 n − 1 2^{n}-1 2n−1 。我们可以枚举 mask ∈ [ 0 , 2 n − 1 ] \\in\\left[0,2^{n}-1\\right] ∈[0,2n−1], mask 的二 进制表示是一个 0 / 1 0 / 1 0/1 序列,我们可以按照这个 0 / 1 0 / 1 0/1 序列在原集合当中取数。当我们枚举完所有 2 n 2^{n} 2n 个 mask, 我们也就能构造出所有的子集。
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
ans = []
n = len(nums)
for mask in range(1<<n): #子集数量 2^n
t = []
for i in range(n): #对每个二进制数也就是每种子集情况都要遍历一边nums数组,
if mask &(1<<i): # 将二进制数中为1的位置在nums中对应的数加入到当前子集中
t.append(nums[i])
ans.append(t)
return ans
回溯算法
当问题需要 “回头”,以此来查找出所有的解的时候,使用回溯算法。即满足结束条件或者发现不是正确路径的时候(走不通),要撤销选择,回退到上一个状态,继续尝试,直到找出所有解为止。
具体请看:回溯算法 python
该题的回溯算法树:
- 以
[1, 2, 3]
为例说明一下回溯的过程 :先看1,选1或不选1,都会再看2,选2或不选2,以此类推。
-
什么都不选,加入空数组
[]
-
选1 :
[1]
,- 选2:
[1,2]
-
- 选3
[1,2,3]
- 选3
- 不选2,选3:
[1,3]
- 选2:
-
不选1,选2:
[2]
-
- 选3
[2,3]
- 选3
选3
[3]
-
用索引index
代表当前递归考察的数字nums[index]
。
当index
越界时,说明所有数字考察完了,得到一个解,把它加入解集,结束当前递归分支。
在进入 b a c k t r a c k ( i n d e x ) backtrack(index) backtrack(index)之前 [ 0 , i n d e x − 1 ] [0,index−1] [0,index−1] 位置的状态是确定的,而 [ i n d e x , n − 1 ] [index,n−1] [index,n−1] 内位置的状态是不确定的, b a c k t r a c k ( i n d e x ) backtrack(index) backtrack(index) 需要确定 i n d e x index index位置的状态,然后求解子问题 b a c k t r a c k ( i n d e x + 1 ) backtrack(index+1) backtrack(index+1)
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
ans = []
tem = []
n = len(nums)
def backtrack(index = 0):
"""
"""
if index == n:# 所有数都填完了
ans.append(tem[:])
return
# 每位上的数字都有选和不选两种 不选:tem+[]; 选:tem.append(nums[index])
tem.append(nums[index])
backtrack(index+1)
tem.pop()
backtrack(index+1) #不取
backtrack(0)
return ans
刚才的思路是:逐个考察数字,每个数都选或不选。等到递归结束时,把集合加入解集。
换一种思路:在执行子递归之前,加入解集,即,在递归压栈前 “做事情”。
该题的回溯算法树:
- 以
[1, 2, 3]
为例说明一下回溯的过程
- 加入空数组
[]
- 枚举长度为1的子集:
[1] | [2] | [3]
- 在长度为1的子集基础上递归进入枚举长度为2的子集:
[1,2] [1,3] | [2,3]
- 在长度为2的子集基础上递归进入枚举长度为3的子集:
[1,2,3]
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
res = []
n = len(nums)
def backtrack(index, tem):
res.append(tem[:])
for j in range(index, n):
tem.append(nums[j])
backtrack(j + 1,tem)
tem.pop()
backtrack(0, [])
return res
参考
以上是关于78. 子集的主要内容,如果未能解决你的问题,请参考以下文章