回溯算法:
回溯算法是一种通过探索所有可能的候选解来找出所有解的问题的算法。如果候选解被确认不是一个有效的解或者不可能产生一个有效的解,回溯算法会丢弃该候选解。这种算法经常用于解决组合问题,如图的遍历、数独、八皇后问题等。
基本原理
- 选择:从候选解中选择一个可能的分支。
- 约束:检查这个分支是否符合约束条件,如果不符合,则剪枝(即放弃这个分支)。
- 目标:检查是否达到了问题的解,如果达到了,则记录这个解。
- 回溯:返回上一步,撤销最近的选择,并尝试另一个分支。
算法步骤
- 初始化:定义一个空的解集。
- 从根开始:从问题的根部开始,探索可能的候选解。
- 深度优先搜索:对每一步,尝试所有可能的选项,深入到下一层。
- 检查约束:在每一步检查约束条件,如果当前分支不能产生有效解,就回溯。
- 记录解:当找到一个有效解时,将其添加到解集中。
- 回溯继续:回溯到上一步,尝试其他分支。
特点
- 时间复杂度:通常很高,因为它尝试所有可能的解决方案,但通过剪枝可以大幅度减少搜索空间。
- 空间复杂度:由于需要存储递归函数的调用栈,空间复杂度也可能很高。
- 适用性:适合于问题的解空间是离散且有限的情况。
应用实例
- 八皇后问题:在8×8的棋盘上放置八个皇后,使得它们互不攻击。
- 数独:填充9×9的网格,使得每一行、每一列和每一个3×3的子网格中的数字1到9不重复。
- 图的遍历:在图结构中寻找特定的路径或者遍历所有的节点。
实现技巧
- 使用递归来实现深度优先搜索。
- 在递归之前和之后加入检查和回溯的逻辑。
- 使用剪枝技术来减少不必要的搜索。
回溯算法的关键在于找到有效的剪枝条件,这样可以显著减少搜索空间,提高算法的效率。
77. 组合
class Solution:
def backtracking(self, n, k, start):
if len(self.path) == k:
self.res.append(self.path[:])
return
for i in range(start, n + 1):
self.path.append(i)
self.backtracking(n,k,i + 1)
self.path.pop()
def combine(self, n: int, k: int) -> List[List[int]]:
self.res = []
self.path = []
self.backtracking(n,k,1)
return self.res
剪枝思路1:在for循环中,实际不需要到末尾,只要到窗口右端碰到n的末尾即可。
class Solution:
def backtracking(self, n, k, start):
if len(self.path) == k:
self.res.append(self.path[:])
return
for i in range(start, n - (k - len(self.path)) + 2):
self.path.append(i)
self.backtracking(n,k,i + 1)
self.path.pop()
def combine(self, n: int, k: int) -> List[List[int]]:
self.res = []
self.path = []
self.backtracking(n,k,1)
return self.res
剪枝思路2:如果当前组合的长度加上从当前数字到 n 的数字总数仍然小于 k,那么就没有必要继续探索这个分支。
if len(path) + (n - start + 1) < k:
return
今日总结:
学习了回溯算法,有点晕掉,回滚几次多理解吧..
本文详细介绍了回溯算法的基本原理,包括选择、约束、目标和回溯步骤,以及其在八皇后问题、数独和图的遍历中的应用。讨论了算法的时间和空间复杂度,以及实现时的剪枝技巧,最后提到通过理解递归和优化剪枝来提高效率。
247

被折叠的 条评论
为什么被折叠?



