Leetcode 回溯 1
LeetCode 46. 全排列 ↑
题目
给定一个不含重复数字的数组 nums,返回其所有可能的全排列。你可以按任意顺序返回答案。
测试样例
-
示例 1:
nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] -
示例 2:
nums = [0,1]
输出:[[0,1],[1,0]] -
示例 3:
nums = [1]
输出:[[1]]
提示
1 ≤ len(nums) ≤ 6-10 ≤ nums[i] ≤ 10nums中整数互不相同
代码(Python):交换法回溯
固定前缀 nums[0:first],从 nums[first:] 中依次选元素换到位置 first,再递归填 first+1;递归返回后交换还原,保证同一层枚举不破坏兄弟分支。当 first == n 时,当前 nums 即一个排列,拷贝入结果。
class Solution(object):
def permute(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
def backtrace(first=0):
if first == n:
res.append(nums[:])
for i in range(first, n):
nums[first], nums[i] = nums[i], nums[first]
backtrace(first + 1)
nums[first], nums[i] = nums[i], nums[first]
n = len(nums)
res = []
backtrace()
return res
讲解视频(讲解视频文件 0405_1.mp4)
思路解析
- 回溯:每层在
i ∈ [first, n)上尝试把nums[i]换到first,进入子问题first+1,回溯前再换回来,避免状态串味。 - 终止:
first == n时说明每一位都已选定,将nums的拷贝加入res(须用nums[:],勿直接append(nums))。 - 复杂度:共
n!个排列,每个拷贝长度n,时间 O(n · n!),递归栈 O(n)。
LeetCode 78. 子集 ↑
题目
给定整数数组 nums,其中元素互不相同。返回 nums 所有可能的子集(幂集)。解集不能包含重复的子集;子集顺序任意。
测试样例
-
示例 1:
nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]] -
示例 2:
nums = [0]
输出:[[],[0]]
提示
1 ≤ len(nums) ≤ 10-10 ≤ nums[i] ≤ 10nums中元素互不相同
代码(Python)解法一:二进制枚举(掩码)
共 2^N 个子集,用 0 … 2^N - 1 的整数 i 表示掩码:第 j 位为 1 表示子集中包含 nums[j]。将 i 转为二进制串后须左补零到长度 N(zfill(N)),再与 nums[0] … nums[N-1] 一一对应;若仅用 bin(i)[2:] 不补零,位数不足时与下标会错位。
class Solution(object):
def subsets(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
N = len(nums)
res = []
itera = 2 ** N
for i in range(itera):
mask = bin(i)[2:].zfill(N)
temp = []
for j in range(N):
if mask[j] == "1":
temp.append(nums[j])
res.append(temp)
return res
思路解析(解法一)
- 一一对应:
N个元素选或不选,共2^N种,与0 … 2^N-1的二进制表示一一对应。 - 实现:
mask[j] == '1'则把nums[j]加入当前子集。 - 复杂度:时间 O(n · 2^n),结果列表规模亦为 O(n · 2^n);空间除答案外 O(n) 临时数组。
代码(Python)解法二:DFS(选或不选)
按下标 cur 依次考虑 nums[cur]:先选(入栈后递归 cur+1,回溯 pop),再不选(直接递归 cur+1)。cur == len(nums) 时当前路径 t 即一个子集,拷贝进答案。
class Solution(object):
def subsets(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
self.t = []
self.ans = []
def dfs(cur):
if cur == len(nums):
self.ans.append(self.t[:])
return
self.t.append(nums[cur])
dfs(cur + 1)
self.t.pop()
dfs(cur + 1)
dfs(0)
return self.ans
思路解析(解法二)
- 决策树:每个元素分叉「放入当前子集 / 不放入」,叶深
n时得到一条完整选择,共2^n条路径。 - 拷贝:
self.ans.append(self.t[:])须复制列表,否则后续修改self.t会污染已加入的结果。 - 复杂度:时间 O(n · 2^n),递归栈 O(n),与解法一同阶。

浙公网安备 33010602011771号