https://leetcode.cn/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/
做回溯的最关键的问题:1、递归参数是什么,2、终止条件是什么
46. 全排列(中等)
47. 全排列 II(中等):思考为什么造成了重复,如何在搜索之前就判断这一支会产生重复;
39. 组合总和(中等)
40. 组合总和 II(中等)
77. 组合(中等)
78. 子集(中等)
90. 子集 II(中等):剪枝技巧同 47 题、39 题、40 题;
60. 第 k 个排列(中等):利用了剪枝的思想,减去了大量枝叶,直接来到需要的叶子结点;
93. 复原 IP 地址(中等)
递归参数为数组中是否被使用的标志
终止条件为path的大小等于nums
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
path = []
res = []
def dfs(used):
if len(path) == len(nums):
res.append(path[:])
for j in range(len(nums)):
if used[j] == True: continue
path.append(nums[j])
used[j] = True
dfs(used)
path.pop()
used[j] = False
used = [False for _ in range(len(nums))]
dfs(used)
return res
需要对重复的排列进行剪枝
与上题的唯一差异就在于多了剪枝判断
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
used = [False for _ in range(len(nums))]
path = []
res = []
def dfs(used):
if len(path) == len(nums):
res.append(path[:])
return
for i in range(len(nums)):
if used[i]: continue
if i>0 and nums[i] == nums[i-1] and used[i-1] == False: continue
path.append(nums[i])
used[i] = True
dfs(used)
used[i] = False
path.pop()
used = [False for _ in range(len(nums))]
nums.sort()
dfs(used)
return res
直接递归就可以
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
res = []
path = []
def dfs(i):
if len(path) == k:
res.append(path[:])
for j in range(i,n+1):
path.append(j)
dfs(j+1)
path.pop()
dfs(1)
return res
递归和dp相辅相成,此题既可以用递归暴力,也可以使用dp来求解。但是注意的是,dp返回的是组合的数量,无法返回组合分别是什么
递归参数:target
终止条件: target==0
难点是在于如何对重复的结果进行剪枝,保持有序搜索,在搜索的过程中去重
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
path = []
res = []
def dfs(j,target):
if target == 0:
res.append((path[:]))
if target<0: return
for i in range(j,len(candidates)):
path.append(candidates[i])
dfs(i,target - candidates[i])
path.pop()
dfs(0,target)
return res
是用排序+used来剔除重复遍历的情况,由于是每个数字只能使用一次,所以递归参数为j+1而非上一题的j。
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
path = []
res = []
def dfs(i,target,used):
if target == 0:
res.append(path[:])
return
if target <0:
return
for j in range(i,len(candidates)):
if j>0 and candidates[j] == candidates[j-1] and used[j-1] == False: continue
path.append(candidates[j])
used[j] = True
dfs(j+1,target-candidates[j],used)
path.pop()
used[j] = False
candidates = sorted(candidates)
used = [False for _ in range(len(candidates))]
dfs(0,target,used)
return res
只使用数字1到9
每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
res = []
path = []
def dfs(i):
if sum(path) == n and len(path) == k:
res.append(path[:])
return
if len(path)>=k: return
for j in range(i,10):
path.append(j)
dfs(j+1)
path.pop()
dfs(1)
return res
题目数据保证答案符合 32 位整数范围。
这题也可以利用递归来求解,但是时间复杂度太高。可以通过dp的方式快速求解得到
使用dp时要注意内外层循环,外层时targte,内层时nums,直接使用一维dp即可
class Solution:
def combinationSum4(self, nums: List[int], target: int) -> int:
res = []
path = []
def dfs(i,target):
if target == 0:
res.append(path[:])
return
if target<0:
return
for j in range(len(nums)):
path.append(nums[j])
dfs(j,target - nums[j])
path.pop()
dfs(0,target)
return len(res)
class Solution:
def combinationSum4(self, nums: List[int], target: int) -> int:
dp = [0 for _ in range(target+1)]
dp[0] = 1
for j in range(target+1):
for i in range(len(nums)):
if j-nums[i]>=0:
dp[j] += dp[j-nums[i]]
return dp[-1]
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
res = []
def dfs(i,path):
res.append(path[:])
for j in range(i,len(nums)):
path.append(nums[j])
dfs(j+1,path)
path.pop()
dfs(0,[])
return res
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
剪枝,去重,和之前的处理策略是一致的
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
res = []
used = [False for _ in range(len(nums))]
def dfs(i,path,used):
res.append(path[:])
for j in range(i,len(nums)):
if j>0 and nums[j] == nums[j-1] and used[j-1] == False: continue
path.append(nums[j])
used[j] = True
dfs(j+1,path,used)
path.pop()
used[j] = False
nums = sorted(nums)
dfs(0,[],used)
return res