回溯法

原文:https://my.oschina.net/u/3024426/blog/4689026

回溯法(Back Tracking Method)(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为 “回溯点”。

可以把回溯法看成是递归调用的一种特殊形式。

代码方面,回溯算法的框架:

result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return

for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择

其核心就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」,特别简单。

总结就是:

循环 + 递归 = 回溯

引言

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就 “回溯” 返回,尝试别的路径。

回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为 “回溯点”。

许多复杂的,规模较大的问题都可以使用回溯法,有 “通用解题方法” 的美称。

算法思想

回溯(backtracking) 是一种系统地搜索问题解答的方法。为了实现回溯,首先需要为问题定义一个解空间(solution space),这个空间必须至少包含问题的一个解(可能是最优的)。下一步是组织解空间以便它能被容易地搜索。典型的组织方法是图 (迷宫问题) 或树 (N 皇后问题)。一旦定义了解空间的组织方法,这个空间即可按深度优先的方法从开始节点进行搜索。

回溯方法的步骤如下:

  • 定义一个解空间,它包含问题的解。
  • 用适于搜索的方式组织该空间。
  • 用深度优先法搜索该空间,利用限界函数避免移动到不可能产生解的子空间。

回溯算法的一个有趣的特性是在搜索执行的同时产生解空间。在搜索期间的任何时刻,仅保留从开始节点到当前节点的路径。因此,回溯算法的空间需求为 O(从开始节点起最长路径的长度)。这个特性非常重要,因为解空间的大小通常是最长路径长度的指数或阶乘。所以如果要存储全部解空间的话,再多的空间也不够用。

 

这段代码实现了经典的回溯算法来生成一个数组的所有排列。让我们逐步分析代码的执行过程,以确定最终的控制台输出。

  1. 初始化数组和结果列表:
    • int[] nums = { 1, 2, 3 }; 初始化一个包含三个元素的数组。
    • List<List<int>> result = new List<List<int>>(); 初始化一个空的列表来存储所有的排列结果。
  2. 调用回溯函数:
    • Backtrack(nums, new List<int>(), result); 从空列表开始调用回溯函数。
  3. 回溯函数的工作原理:
    • 终止条件:如果 tempList.Count == nums.Length,即临时列表的长度等于原数组的长度,说明已经找到了一个完整的排列,将其添加到结果列表中。
    • 选择:遍历数组 nums 中的每个元素,如果当前元素不在 tempList 中,则将其添加到 tempList 中。
    • 递归调用:对更新后的 tempList 进行递归调用,继续构建排列。
    • 撤销选择:递归调用返回后,移除最后添加的元素,以尝试其他可能的排列。
  4. 遍历结果列表并打印:
    • 使用 foreach 循环遍历结果列表 result,并将每个排列转换为字符串格式输出。

排列生成过程:

  • 初始状态:tempList = []
  • 第一次选择:可以选择 1, 2, 或 3。
    • 选择 1:tempList = [1],递归。
      • 选择 2:tempList = [1, 2],递归。
        • 选择 3:tempList = [1, 2, 3],找到一个排列 [1, 2, 3]
        • 撤销选择 3,tempList = [1, 2]
      • 选择 3:tempList = [1, 3],递归。
        • 选择 2:tempList = [1, 3, 2],找到一个排列 [1, 3, 2]
        • 撤销选择 2,tempList = [1, 3]
      • 撤销选择 3,tempList = [1]
    • 选择 2:tempList = [2],递归。
      • 选择 1:tempList = [2, 1],递归。
        • 选择 3:tempList = [2, 1, 3],找到一个排列 [2, 1, 3]
        • 撤销选择 3,tempList = [2, 1]
      • 选择 3:tempList = [2, 3],递归。
        • 选择 1:tempList = [2, 3, 1],找到一个排列 [2, 3, 1]
        • 撤销选择 1,tempList = [2, 3]
      • 撤销选择 3,tempList = [2]
    • 选择 3:tempList = [3],递归。
      • 选择 1:tempList = [3, 1],递归。
        • 选择 2:tempList = [3, 1, 2],找到一个排列 [3, 1, 2]
        • 撤销选择 2,tempList = [3, 1]
      • 选择 2:tempList = [3, 2],递归。
        • 选择 1:tempList = [3, 2, 1],找到一个排列 [3, 2, 1]
        • 撤销选择 1,tempList = [3, 2]
      • 撤销选择 2,tempList = [3]
  • 撤销选择 2,tempList = []
  • 撤销选择 1,回到初始状态。

控制台输出:

复制代码
  1, 2, 3
  1, 3, 2
  2, 1, 3
  2, 3, 1
  3, 1, 2
  3, 2, 1

 

 

posted @ 2025-02-09 23:36  MaxBruce  阅读(48)  评论(0)    收藏  举报