定义
采用试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。
回溯法通常用最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种情况:
- 找到一个可能存在的正确的答案;
- 在尝试了所有可能的分步方法后宣告该问题没有答案。
搜索与遍历
我们每天使用的搜索引擎帮助我们在庞大的互联网上搜索信息。搜索引擎的「搜索」和「回溯搜索」算法里「搜索」的意思是一样的。
搜索问题的解,可以通过 遍历 实现。所以很多教程把「回溯算法」称为爆搜(暴力解法)。因此回溯算法用于 搜索一个问题的所有的解 ,通过深度优先遍历的思想实现。
回溯模板
def backward():
if (回朔点):# 这条路走到底的条件。也是递归出口
保存该结果
return
for route in all_route_set : 逐步选择当前节点下的所有可能route
if 剪枝条件:
剪枝前的操作
return #不继续往下走了,退回上层,换个路再走
else:#当前路径可能是条可行路径
保存当前数据 #向下走之前要记住已经走过这个节点了。例如push当前节点
self.backward() #递归发生,继续向下走一步了。
回朔清理 # 该节点下的所有路径都走完了,清理堆栈,准备下一个递归。例如弹出当前节点
全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
解答
先写以 11 开头的全排列,它们是:[1, 2, 3], [1, 3, 2],即 1 + [2, 3] 的全排列(注意:递归结构体现在这里);
再写以 22 开头的全排列,它们是:[2, 1, 3], [2, 3, 1],即 2 + [1, 3] 的全排列;
最后写以 33 开头的全排列,它们是:[3, 1, 2], [3, 2, 1],即 3 + [1, 2] 的全排列。
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
ans = []
def trace(nums, temp):
if not nums:
ans.append(temp)
return
for i, n in enumerate(nums):
trace(nums[:i] + nums[i+1:], temp + [n])
trace(nums, [])
return ans
排列组合
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
解答:
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
candidates = sorted(candidates)
ans = []
def find(i, use, remain):
for index in range(i, len(candidates)):
n = candidates[index]
if remain == n:
ans.append(use + [n])
elif remain > n:
find(index, use + [n], remain - n)
else:
return
find(0, [], target)
return ans
组合
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
解答:
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
ans, track = [], []
self.backtrack(n, k, 1, track, ans)
return ans
def backtrack(self, n, k, start, track, ans):
if len(track) == k:
ans.append(track[:])
for i in range(start, n + 1):
track.append(i)
self.backtrack(n, k, i + 1, track, ans)
track.pop()
子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
解答:
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
ans = []
length = len(nums)
def find(index, temp):
ans.append(temp)
for i in range(index, length):
find(i + 1, temp + [nums[i]])
find(0, [])
return ans