子集型回溯模版1
[1, 2] -> [ [], [1], [2], [1, 2] ]
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
ans = []
def dfs(i, path):
ans.append(path[:])
for j in range(i, n):
path.append(nums[j])
dfs(j+1, path)
path.pop()
dfs(0, [])
return ans
i为目前的位置, j为下一个考虑的位置. 这种情况就应该把每一步都加到ans里, 而不是等到i==n
假设存在dup, 则我们应该在横向去重(for loop里)
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
nums.sort() # 首先对数组进行排序
n = len(nums)
ans = []
def dfs(i, path):
ans.append(path[:])
for j in range(i, n):
if j > i and nums[j] == nums[j-1]:
continue # 跳过重复元素
path.append(nums[j])
dfs(j+1, path)
path.pop()
dfs(0, [])
return ans
77 组合型回溯模版
n choose k
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
nums = list(range(1, n + 1))
ans = []
def dfs(i, path):
if len(path) == k:
ans.append(path[:])
return
for j in range(i, n):
path.append(nums[j])
dfs(j+1, path)
path.pop()
dfs(0, [])
return ans
只能说思路和子集一模一样, 加入ans的条件变了而已
排列(permutation)型
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
ans = []
n = len(nums)
def dfs(path):
if len(path) == n:
ans.append(path[:])
return
for i in range(n):
if nums[i] in path:
continue
path.append(nums[i])
dfs(path)
path.pop()
dfs([])
return ans
有重复元素的permutation
同样也是横向去重, 所以我们在for loop前创建一个set来表示已经被用过的元素
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
ans = []
n = len(nums)
nums.sort() # 排序以便于处理重复元素
def dfs(path, indices):
if len(path) == n:
ans.append(path[:])
return
used = set() # 用于跟踪在当前位置使用过的元素
for index in list(indices): # 转换为列表以便在循环中修改
if nums[index] in used:
continue # 跳过在当前位置已经使用过的重复元素
used.add(nums[index])
path.append(nums[index])
new_indices = indices.copy()
new_indices.remove(index)
dfs(path, new_indices)
path.pop()
initial_indices = set(range(n))
dfs([], initial_indices)
return ans
力扣知识点: 看是横向遍历还是纵向遍历
好题: 77, 39, 78, 46
i+1和start + 1的根本区别: 考不考虑当前元素重复加进去, 可以看40和78的区别
python知识点
result.append(path[:])和 result.append(path[:])
区别是前者拷贝, 后者引用
能解决的问题
组合问题:N个数里面按一定规则找出k个数的集合
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
排列问题:N个数按一定规则全排列,有几种排列方式
棋盘问题:N皇后,解数独等等
通用模版
void backtracking(当前参数已经选了哪些) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}