菜单

回溯

相关源文件

回溯是一种系统性的算法范式,它通过探索所有可能的解决方案来解决问题。它通过逐步构建解决方案的候选者,并在确定候选者不可能导向有效解决方案时放弃该候选者(“回溯”)。本页面解释了回溯算法的核心概念、实现模式及其应用。

有关动态规划的信息,它通常可以解决一些可以采用回溯方法解决的问题,但效率特征不同,请参阅动态规划初步探索

核心概念

回溯算法可以看作是对表示所有可能解决方案的状态空间的深度优先搜索。算法分步做出了一系列选择,试图逐步构建解决方案。当它确定当前选择无法导向有效解决方案时,它会“回溯”到前一个状态并做出不同的选择。

来源:[docs/chapter_backtracking/backtracking_algorithm.md:1-5]

尝试与回溯

回溯的本质在于“尝试”与“回溯”的过程

  1. 尝试:做出一个选择,然后移至下一个决策点。
  2. 回溯:当一条路径无法导向有效解决方案时,撤销该选择并返回到前一个状态。

这可以通过一个简单的二叉树遍历问题来说明:在二叉树中查找值为 7 的节点的路径。

当我们访问节点 7 时,我们会记录路径并继续探索。当我们到达叶节点或完成一个分支的探索后,我们会通过从路径中删除最后一个节点来进行回溯,并尝试另一条分支。

来源:[docs/chapter_backtracking/backtracking_algorithm.md:18-62]

剪枝

剪枝是一种通过避免不必要的探索某些分支来提高效率的技术。当我们认识到某些分支根据问题约束不可能导向有效解决方案时,我们可以对其进行剪枝。

例如,如果我们正在寻找值为 7 的节点路径,但有一个约束条件是路径不能包含值为 3 的节点,那么我们可以立即剪掉任何以节点 3 开始的分支。

来源:[docs/chapter_backtracking/backtracking_algorithm.md:74-90]

回溯框架

大多数回溯问题都可以使用遵循此模式的标准模板来解决

回溯算法中的核心函数包括

  1. is_solution(state):确定当前状态是否为有效解决方案
  2. record_solution(state, result):记录解决方案
  3. is_valid(state, choice):检查在当前状态下某个选择是否有效
  4. make_choice(state, choice):通过做出选择来更新状态
  5. undo_choice(state, choice):将状态恢复到做出选择之前的状态

来源:[docs/chapter_backtracking/backtracking_algorithm.md:92-404]

框架实现

通用的回溯框架可以实现如下

backtrack(state, choices, result):
    # Check if current state is a solution
    if is_solution(state):
        record_solution(state, result)
        return
    
    # Try each possible choice
    for choice in choices:
        # Pruning: only consider valid choices
        if is_valid(state, choice):
            # Try this choice
            make_choice(state, choice)
            # Recursive call
            backtrack(state, choices, result)
            # Backtrack
            undo_choice(state, choice)

此模板可用于解决各种回溯问题。

来源:[docs/chapter_backtracking/backtracking_algorithm.md:98-404]

优点和局限性

优点

  • 可以找到问题的所有可能解决方案
  • 概念上易于理解和实现
  • 通过有效的剪枝可以高效地解决问题

局限性

回溯算法存在显著的时间和空间复杂度限制

  • 时间:通常是指数级或阶乘级复杂度(O(2^n) 或 O(n!))
  • 空间:可能需要大量的内存用于递归栈

对于大型或复杂的问题,如果不进行优化,回溯可能会变得不切实际。

来源:[docs/chapter_backtracking/backtracking_algorithm.md:469-482]

常见优化技术

回溯的两种主要优化技术

  1. 剪枝:尽早消除无法导向有效解决方案的分支
  2. 启发式搜索:优先探索更有可能产生解决方案的路径

来源:[docs/chapter_backtracking/backtracking_algorithm.md:477-482]

经典回溯问题

排列问题

找出给定元素集的所有可能排列。关键考虑因素包括

  • 处理有重复元素和无重复元素的情况
  • 通过剪枝避免重复选择元素
  • 当输入包含重复项时,通过剪枝避免重复的排列

来源:[docs/chapter_backtracking/permutations_problem.md:1-95]

子集和问题

找出所有和为目标值的元素子集。挑战包括

  • 处理可以多次使用与只能使用一次的元素
  • 当输入包含重复项时,消除重复的子集
  • 通过排序和提前终止优化搜索

来源:[docs/chapter_backtracking/subset_sum_problem.md:1-95]

N皇后问题

将 N 个皇后放置在 N×N 的棋盘上,使得任意两个皇后都不能互相攻击。关键方面

  • 使用列和对角线约束进行剪枝
  • 逐行放置策略
  • 有效的约束检查

来源:[docs/chapter_backtracking/n_queens_problem.md:1-53]

与其他算法的关系

回溯与其他搜索算法在重要方面有所不同

算法方法优化应用程序
回溯尝试所有可能性并通过系统剪枝剪枝那些无法导向有效解决方案的分支找出所有满足约束的解决方案
动态规划将问题分解为重叠的子问题通过存储结果避免重复计算子问题具有重叠子问题的优化问题
贪心算法在每一步做出局部最优选择如果选择导向次优解决方案,则不回溯具有贪心选择性质的问题
分治法分解为独立的子问题组合子问题解决方案没有重叠子问题的特征

来源:[docs/chapter_dynamic_programming/dp_problem_features.md:3-9], [docs/chapter_dynamic_programming/intro_to_dynamic_programming.md:21-25]

常用术语

理解标准术语有助于分析回溯问题

术语定义示例
解决方案满足所有问题约束的答案N 个皇后的一个有效布局
约束限制有效解决方案的条件皇后不能相互攻击
状态管理表示搜索的当前进度当前的棋盘部分布局
尝试探索一个选择的过程在位置 (i,j) 放置皇后
回溯撤销一个选择以尝试其他选项当放置皇后导致冲突时将其移除
剪枝跳过无法导向解决方案的分支不探索已放置皇后的列

来源:[docs/chapter_backtracking/backtracking_algorithm.md:451-464]

结论

回溯是一种强大的算法范式,用于在避免不必要探索的同时,穷举搜索解决方案空间。它对于约束满足问题、搜索问题和组合优化问题特别有效,当应用了适当的剪枝技术时。尽管可能具有指数级的时间复杂度,但对于许多需要找到所有可能解决方案的问题,回溯仍然是最有效的方法。