李宪军 發表於 2025-12-29 09:00:00

回溯算法总结

<h2 id="概述">概述</h2>
<p>其实回溯算法和我们常说的 DFS 算法非常类似,本质上就是一种暴力穷举算法。回溯算法和 DFS 算法的细微差别是:回溯算法是在遍历「树枝」,DFS 算法是在遍历「节点」</p>
<p><strong>抽象地说,解决一个回溯问题,实际上就是遍历一棵决策树的过程,树的每个叶子节点存放着一个合法答案。你把整棵树遍历一遍,把叶子节点上的答案都收集起来,就能得到所有的合法答案</strong>。</p>
<p>站在回溯树的一个节点上,你只需要思考 3 个问题:</p>
<p>1、路径:也就是已经做出的选择。</p>
<p>2、选择列表:也就是你当前可以做的选择。</p>
<p>3、结束条件:也就是到达决策树底层,无法再做选择的条件。</p>
<p>代码方面,回溯算法的框架:</p>
<pre><code class="language-java">result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
      result.add(路径)
      return
   
    for 选择 in 选择列表:
      做选择
      backtrack(路径, 选择列表)
      撤销选择
</code></pre>
<p>for循环就是遍历集合区间,可以理解一个节点有多少个孩子,这个for循环就执行多少次。</p>
<p>backtracking这里自己调用自己,实现递归。</p>
<p><strong>其核心就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」</strong></p>
<h2 id="回溯算法解决组合问题">回溯算法解决组合问题</h2>
<p>这里的组合问题 元素无重不可复选</p>
<pre><code class="language-java">class Solution {

    List&lt;List&lt;Integer&gt;&gt; res = new LinkedList&lt;&gt;();
    // 记录回溯算法的递归路径
    LinkedList&lt;Integer&gt; track = new LinkedList&lt;&gt;();

    // 主函数
    public List&lt;List&lt;Integer&gt;&gt; combine(int n, int k) {
      backtrack(1, n, k);
      return res;
    }

    void backtrack(int start, int n, int k) {
      // base case
      if (k == track.size()) {
            // 遍历到了第 k 层,收集当前节点的值
            res.add(new LinkedList&lt;&gt;(track));
            return;
      }
      
      // 回溯算法标准框架
      for (int i = start; i &lt;= n; i++) {
            // 选择
            track.addLast(i);
            // 通过 start 参数控制树枝的遍历,避免产生重复的子集
            backtrack(i + 1, n, k);
            // 撤销选择
            track.removeLast();
      }
    }
}
</code></pre>
<h2 id="回溯算法解决排列问题">回溯算法解决排列问题</h2>
<pre><code class="language-java">class Solution {

    List&lt;List&lt;Integer&gt;&gt; res = new LinkedList&lt;&gt;();
    // 记录回溯算法的递归路径
    LinkedList&lt;Integer&gt; track = new LinkedList&lt;&gt;();
    // track 中的元素会被标记为 true
    boolean[] used;

    /* 主函数,输入一组不重复的数字,返回它们的全排列 */
    public List&lt;List&lt;Integer&gt;&gt; permute(int[] nums) {
      used = new boolean;
      backtrack(nums);
      return res;
    }

    // 回溯算法核心函数
    void backtrack(int[] nums) {
      // base case,到达叶子节点
      if (track.size() == nums.length) {
            // 收集叶子节点上的值
            res.add(new LinkedList(track));
            return;
      }

      // 回溯算法标准框架
      for (int i = 0; i &lt; nums.length; i++) {
            // 已经存在 track 中的元素,不能重复选择
            if (used) {
                continue;
            }
            // 做选择
            used = true;
            track.addLast(nums);
            // 进入下一层回溯树
            backtrack(nums);
            // 取消选择
            track.removeLast();
            used = false;
      }
    }
}
</code></pre>
<h2 id="总结">总结</h2>
<p>回溯算法就是个多叉树的遍历问题,关键就是在前序遍历和后序遍历的位置做一些操作,算法框架如下:</p>
<pre><code class="language-python">def backtrack(...):
    for 选择 in 选择列表:
      做选择
      backtrack(...)
      撤销选择
</code></pre>
<p><strong>写 <code>backtrack</code> 函数时,需要维护走过的「路径」和当前可以做的「选择列表」,当触发「结束条件」时,将「路径」记入结果集</strong>。</p>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自在线网站:seven的菜鸟成长之路,作者:seven,转载请注明原文链接:www.seven97.top</p><br><br>
来源:https://www.cnblogs.com/sevencoding/p/19410967
頁: [1]
查看完整版本: 回溯算法总结