
| 题目 | 决策 | 递归结构 |
|---|---|---|
| 全排列 | 每层必须选一个 | 深度固定 |
| 子集 | 每个元素选/不选 | 深度递增 |
每个元素都有两种选择:选 或 不选。
python
[]
├── [1]
│ ├── [1,2]
│ │ └── [1,2,3]
│ └── [1,3]
├── [2]
│ └── [2,3]
└── [3]
切片回溯
- 排列:顺序重要 → 所有剩余都可选
- 组合 / 子集:顺序不重要 → 只能往后选
| 问题类型 | 下一层还能选谁 | 代码 |
|---|---|---|
| 排列 | 除自己以外所有 | nums[:i] + nums[i+1:] |
| 子集 / 组合 | 只选后面的 | nums[i+1:] |
python
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
res = []
def backtrack(nums, tmp): #(待选列表,已有排列)
res.append(tmp) # 当前路径就是一个子集
# 依次选择待选列表的元素,进入排列。
for i in range(len(nums)):
backtrack(nums[i+1:], tmp + [nums[i]]) # 子集顺序不重要,只能往后选
backtrack(nums, [])
return res
只能向后选:
python
[]
├── [1]
│ ├── [1,2]
│ │ └── [1,2,3]
│ └── [1,3]
├── [2]
│ └── [2,3]
└── [3]
如果数组长度为 n,子集总数 = 2ⁿ
理论最优:O(n × 2ⁿ)
简单写法(位运算法)
每个元素可以选或不选,可以用二进制表示:
python
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
res = []
n = len(nums)
# 数组长度为 n,子集总数 = 2ⁿ,则一共有2ⁿ种mask模板,2ⁿ=(1 << n) (左移)
for mask in range(1 << n):
subset = [] # 这个mask产生的子集
# 对每种mask,数组的n位
for i in range(n): # 对nums[0], nums[1], nums[2]...
if mask & (1 << i): # 判断该位是否为 1
subset.append(nums[i]) # 数字加入子集
res.append(subset) # 子集加入答案
return res
时间复杂度
外层循环:2ⁿ
内层循环:n
| 方法 | 时间复杂度 | 额外空间 |
|---|---|---|
| 二进制位图 | O(n·2ⁿ) | O(n) |
| 回溯 | O(n·2ⁿ) | O(n) |