78.子集
1.题目
2.我的解决方案
太菜了,这道题用的思路不太会
3.官方的解决方案
回溯法:从选择和不选择的角度出发
- 看视频:点击查看
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
# 采用回溯法,使用深度优先遍历
# 二叉树所有的叶子结点都是问题的解
def dfs(nums, length, index, path, res):
if index == length: # 到了递归的叶子结点
res.append(path[:]) # 将路径状态添加到结果当中(必须是path[:], 不能只是path))
return
dfs(nums, length, index+1, path, res) # 不考虑该元素,进行下一层递归
path.append(nums[index]) # 考虑该元素,就将其加入路径变量
dfs(nums, length, index+1, path, res)
path.pop() # 回溯算法状态重置,在递归前做了什么操作,就在递归后做相反的操作
length = len(nums)
self.res = []
path = []
dfs(nums, length, 0, path, self.res) # 输入数组,输入数组的长度,当前考虑的元素的下标,传入路径变量(栈),结果
return self.res
- 时间复杂度: O ( n ∗ 2 n ) O(n*2^n) O(n∗2n) :形成二叉树的所有 2 n + 1 2^{n+1} 2n+1结点 O ( 2 n ) O(2^n) O(2n),保存一次结点所占用的时间为 O ( n ) O(n) O(n)
- 空间复杂度
O
(
n
)
O(n)
O(n) :形成二叉树的所有
2
n
+
1
2^{n+1}
2n+1个结点,每个结点所占用的空间为
O
(
n
)
O(n)
O(n)
关于列表中插入列表遇到的问题解决方案:点击查看
按照求全排列的思路
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
# 采用回溯法,使用深度优先遍历
# 按照全排列的思路,绘制的是多叉树
def dfs(nums, length, begin, path, res):
res.append(path[:]) # 前序遍历
for i in range(begin, length):
# i表示从num[i]为根结点,作为begin进行深度优先遍历,到了叶子结点,i++,换一个num[i]作为根节点,继续向下遍历
path.append(nums[i]) # 考虑该元素,就将其加入路径变量
dfs(nums, length, i+1, path, res)
path.pop() # 回溯算法状态重置,在递归前做了什么操作,就在递归后做相反的操作
length = len(nums)
self.res = []
path = []
dfs(nums, length, 0, path, self.res) # 输入数组,输入数组的长度,当前考虑的元素的下标,传入路径变量(栈),结果
return self.res
- 时间复杂度: O ( n ∗ 2 n ) O(n*2^n) O(n∗2n) :形成二叉树的所有 2 n + 1 2^{n+1} 2n+1结点 O ( 2 n ) O(2^n) O(2n),保存一次结点所占用的时间为 O ( n ) O(n) O(n)
- 空间复杂度 O ( n ) O(n) O(n) :形成二叉树的所有 2 n + 1 2^{n+1} 2n+1个结点,每个结点所占用的空间为 O ( n ) O(n) O(n)
位运算
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
# 采用位运算
# 位运算和子集中元素的选与不选有结构上的巧合
length = len(nums)
size = 1 << length # 子集的个数(左移相当于乘以2)
res = []
for i in range(0, size): # 每个数字都对应了结果集中的一个子集元素
subset = []
for j in range(0, length): # 从nums开始中开始筛选放入
# 对应的i右移j位,来从右到左判断是否选择该元素进入子集
if((i >> j) & 1 == 1): # 说明这个数是子集当中的元素,将其添加到子集当中
subset.append(nums[j])
res.append(subset)
return res