This commit is contained in:
krahets
2023-07-16 04:19:01 +08:00
parent 54b99d13c8
commit dbf682ebc9
110 changed files with 25308 additions and 10305 deletions

View File

@ -25,7 +25,7 @@
<title>12.1.   回溯算法 - Hello 算法</title>
<title>13.1.   回溯算法 - Hello 算法</title>
@ -79,7 +79,7 @@
<div data-md-component="skip">
<a href="#121" class="md-skip">
<a href="#131" class="md-skip">
跳转至
</a>
@ -113,7 +113,7 @@
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
12.1. &nbsp; 回溯算法
13.1. &nbsp; 回溯算法
</span>
</div>
@ -1752,6 +1752,87 @@
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_13" >
<div class="md-nav__link md-nav__link--index ">
<a href="../../chapter_divide_and_conquer/">12. &nbsp; &nbsp; 分治</a>
<label for="__nav_13">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_13_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_13">
<span class="md-nav__icon md-icon"></span>
12. &nbsp; &nbsp; 分治
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../chapter_divide_and_conquer/divide_and_conquer/" class="md-nav__link">
12.1. &nbsp; 分治算法New
</a>
</li>
<li class="md-nav__item">
<a href="../../chapter_divide_and_conquer/build_binary_tree/" class="md-nav__link">
12.2. &nbsp; 构建树问题New
</a>
</li>
</ul>
</nav>
</li>
@ -1761,7 +1842,7 @@
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_13" checked>
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_14" checked>
@ -1784,18 +1865,18 @@
<div class="md-nav__link md-nav__link--index ">
<a href="../">12. &nbsp; &nbsp; 回溯</a>
<a href="../">13. &nbsp; &nbsp; 回溯</a>
<label for="__nav_13">
<label for="__nav_14">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_13_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_13">
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_14_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_14">
<span class="md-nav__icon md-icon"></span>
12. &nbsp; &nbsp; 回溯
13. &nbsp; &nbsp; 回溯
</label>
<ul class="md-nav__list" data-md-scrollfix>
@ -1817,12 +1898,12 @@
<label class="md-nav__link md-nav__link--active" for="__toc">
12.1. &nbsp; 回溯算法
13.1. &nbsp; 回溯算法
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
12.1. &nbsp; 回溯算法
13.1. &nbsp; 回溯算法
</a>
@ -1841,43 +1922,43 @@
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#1211" class="md-nav__link">
12.1.1. &nbsp; 尝试与回退
<a href="#1311" class="md-nav__link">
13.1.1. &nbsp; 尝试与回退
</a>
</li>
<li class="md-nav__item">
<a href="#1212" class="md-nav__link">
12.1.2. &nbsp; 剪枝
<a href="#1312" class="md-nav__link">
13.1.2. &nbsp; 剪枝
</a>
</li>
<li class="md-nav__item">
<a href="#1213" class="md-nav__link">
12.1.3. &nbsp; 常用术语
<a href="#1313" class="md-nav__link">
13.1.3. &nbsp; 常用术语
</a>
</li>
<li class="md-nav__item">
<a href="#1214" class="md-nav__link">
12.1.4. &nbsp; 框架代码
<a href="#1314" class="md-nav__link">
13.1.4. &nbsp; 框架代码
</a>
</li>
<li class="md-nav__item">
<a href="#1215" class="md-nav__link">
12.1.5. &nbsp; 优势与局限性
<a href="#1315" class="md-nav__link">
13.1.5. &nbsp; 优势与局限性
</a>
</li>
<li class="md-nav__item">
<a href="#1216" class="md-nav__link">
12.1.6. &nbsp; 典型例题
<a href="#1316" class="md-nav__link">
13.1.6. &nbsp; 典型例题
</a>
</li>
@ -1898,7 +1979,7 @@
<li class="md-nav__item">
<a href="../permutations_problem/" class="md-nav__link">
12.2. &nbsp; 全排列问题
13.2. &nbsp; 全排列问题
</a>
</li>
@ -1912,7 +1993,7 @@
<li class="md-nav__item">
<a href="../subset_sum_problem/" class="md-nav__link">
12.3. &nbsp; 子集和问题
13.3. &nbsp; 子集和问题
</a>
</li>
@ -1926,7 +2007,7 @@
<li class="md-nav__item">
<a href="../n_queens_problem/" class="md-nav__link">
12.4. &nbsp; N 皇后问题
13.4. &nbsp; N 皇后问题
</a>
</li>
@ -1940,168 +2021,7 @@
<li class="md-nav__item">
<a href="../summary/" class="md-nav__link">
12.5. &nbsp; 小结
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_14" >
<div class="md-nav__link md-nav__link--index ">
<a href="../../chapter_dynamic_programming/">13. &nbsp; &nbsp; 动态规划</a>
<label for="__nav_14">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_14_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_14">
<span class="md-nav__icon md-icon"></span>
13. &nbsp; &nbsp; 动态规划
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../chapter_dynamic_programming/intro_to_dynamic_programming/" class="md-nav__link">
13.1. &nbsp; 初探动态规划New
</a>
</li>
<li class="md-nav__item">
<a href="../../chapter_dynamic_programming/dp_problem_features/" class="md-nav__link">
13.2. &nbsp; DP 问题特性New
</a>
</li>
<li class="md-nav__item">
<a href="../../chapter_dynamic_programming/dp_solution_pipeline/" class="md-nav__link">
13.3. &nbsp; DP 解题思路New
</a>
</li>
<li class="md-nav__item">
<a href="../../chapter_dynamic_programming/knapsack_problem/" class="md-nav__link">
13.4. &nbsp; 0-1 背包问题New
</a>
</li>
<li class="md-nav__item">
<a href="../../chapter_dynamic_programming/unbounded_knapsack_problem/" class="md-nav__link">
13.5. &nbsp; 完全背包问题New
</a>
</li>
<li class="md-nav__item">
<a href="../../chapter_dynamic_programming/edit_distance_problem/" class="md-nav__link">
13.6. &nbsp; 编辑距离问题New
</a>
</li>
<li class="md-nav__item">
<a href="../../chapter_dynamic_programming/summary/" class="md-nav__link">
13.7. &nbsp; 小结New
13.5. &nbsp; 小结
</a>
</li>
@ -2132,31 +2052,53 @@
<label class="md-nav__link" for="__nav_15" id="__nav_15_label" tabindex="0">
14. &nbsp; &nbsp; 附录
<span class="md-nav__icon md-icon"></span>
</label>
<div class="md-nav__link md-nav__link--index ">
<a href="../../chapter_dynamic_programming/">14. &nbsp; &nbsp; 动态规划</a>
<label for="__nav_15">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_15_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_15">
<span class="md-nav__icon md-icon"></span>
14. &nbsp; &nbsp; 附录
14. &nbsp; &nbsp; 动态规划
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../chapter_appendix/installation/" class="md-nav__link">
14.1. &nbsp; 编程环境安装
<a href="../../chapter_dynamic_programming/intro_to_dynamic_programming/" class="md-nav__link">
14.1. &nbsp; 初探动态规划New
</a>
</li>
@ -2169,8 +2111,78 @@
<li class="md-nav__item">
<a href="../../chapter_appendix/contribution/" class="md-nav__link">
14.2. &nbsp; 一起参与创作
<a href="../../chapter_dynamic_programming/dp_problem_features/" class="md-nav__link">
14.2. &nbsp; DP 问题特性New
</a>
</li>
<li class="md-nav__item">
<a href="../../chapter_dynamic_programming/dp_solution_pipeline/" class="md-nav__link">
14.3. &nbsp; DP 解题思路New
</a>
</li>
<li class="md-nav__item">
<a href="../../chapter_dynamic_programming/knapsack_problem/" class="md-nav__link">
14.4. &nbsp; 0-1 背包问题New
</a>
</li>
<li class="md-nav__item">
<a href="../../chapter_dynamic_programming/unbounded_knapsack_problem/" class="md-nav__link">
14.5. &nbsp; 完全背包问题New
</a>
</li>
<li class="md-nav__item">
<a href="../../chapter_dynamic_programming/edit_distance_problem/" class="md-nav__link">
14.6. &nbsp; 编辑距离问题New
</a>
</li>
<li class="md-nav__item">
<a href="../../chapter_dynamic_programming/summary/" class="md-nav__link">
14.7. &nbsp; 小结New
</a>
</li>
@ -2201,6 +2213,75 @@
<label class="md-nav__link" for="__nav_16" id="__nav_16_label" tabindex="0">
15. &nbsp; &nbsp; 附录
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_16_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_16">
<span class="md-nav__icon md-icon"></span>
15. &nbsp; &nbsp; 附录
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../chapter_appendix/installation/" class="md-nav__link">
15.1. &nbsp; 编程环境安装
</a>
</li>
<li class="md-nav__item">
<a href="../../chapter_appendix/contribution/" class="md-nav__link">
15.2. &nbsp; 一起参与创作
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_17" >
@ -2213,8 +2294,8 @@
</div>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_16_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_16">
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_17_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_17">
<span class="md-nav__icon md-icon"></span>
参考文献
</label>
@ -2255,43 +2336,43 @@
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#1211" class="md-nav__link">
12.1.1. &nbsp; 尝试与回退
<a href="#1311" class="md-nav__link">
13.1.1. &nbsp; 尝试与回退
</a>
</li>
<li class="md-nav__item">
<a href="#1212" class="md-nav__link">
12.1.2. &nbsp; 剪枝
<a href="#1312" class="md-nav__link">
13.1.2. &nbsp; 剪枝
</a>
</li>
<li class="md-nav__item">
<a href="#1213" class="md-nav__link">
12.1.3. &nbsp; 常用术语
<a href="#1313" class="md-nav__link">
13.1.3. &nbsp; 常用术语
</a>
</li>
<li class="md-nav__item">
<a href="#1214" class="md-nav__link">
12.1.4. &nbsp; 框架代码
<a href="#1314" class="md-nav__link">
13.1.4. &nbsp; 框架代码
</a>
</li>
<li class="md-nav__item">
<a href="#1215" class="md-nav__link">
12.1.5. &nbsp; 优势与局限性
<a href="#1315" class="md-nav__link">
13.1.5. &nbsp; 优势与局限性
</a>
</li>
<li class="md-nav__item">
<a href="#1216" class="md-nav__link">
12.1.6. &nbsp; 典型例题
<a href="#1316" class="md-nav__link">
13.1.6. &nbsp; 典型例题
</a>
</li>
@ -2319,7 +2400,7 @@
<h1 id="121">12.1. &nbsp; 回溯算法<a class="headerlink" href="#121" title="Permanent link">&para;</a></h1>
<h1 id="131">13.1. &nbsp; 回溯算法<a class="headerlink" href="#131" title="Permanent link">&para;</a></h1>
<p>「回溯算法 Backtracking Algorithm」是一种通过穷举来解决问题的方法它的核心思想是从一个初始状态出发暴力搜索所有可能的解决方案当遇到正确的解则将其记录直到找到解或者尝试了所有可能的选择都无法找到解为止。</p>
<p>回溯算法通常采用「深度优先搜索」来遍历解空间。在二叉树章节中,我们提到前序、中序和后序遍历都属于深度优先搜索。接下来我们先用前序遍历构造一个回溯问题,逐步了解回溯算法的工作原理。</p>
<div class="admonition question">
@ -2463,7 +2544,7 @@
<p><img alt="在前序遍历中搜索节点" src="../backtracking_algorithm.assets/preorder_find_nodes.png" /></p>
<p align="center"> Fig. 在前序遍历中搜索节点 </p>
<h2 id="1211">12.1.1. &nbsp; 尝试与回退<a class="headerlink" href="#1211" title="Permanent link">&para;</a></h2>
<h2 id="1311">13.1.1. &nbsp; 尝试与回退<a class="headerlink" href="#1311" title="Permanent link">&para;</a></h2>
<p><strong>之所以称之为回溯算法,是因为该算法在搜索解空间时会采用“尝试”与“回退”的策略</strong>。当算法在搜索过程中遇到某个状态无法继续前进或无法得到满足条件的解时,它会撤销上一步的选择,退回到之前的状态,并尝试其他可能的选择。</p>
<p>对于例题一,访问每个节点都代表一次“尝试”,而越过叶结点或返回父节点的 <code>return</code> 则表示“回退”。</p>
<p>值得说明的是,<strong>回退并不等价于函数返回</strong>。为解释这一点,我们对例题一稍作拓展。</p>
@ -2679,7 +2760,7 @@
</div>
</div>
</div>
<h2 id="1212">12.1.2. &nbsp; 剪枝<a class="headerlink" href="#1212" title="Permanent link">&para;</a></h2>
<h2 id="1312">13.1.2. &nbsp; 剪枝<a class="headerlink" href="#1312" title="Permanent link">&para;</a></h2>
<p>复杂的回溯问题通常包含一个或多个约束条件,<strong>约束条件通常可用于“剪枝”</strong></p>
<div class="admonition question">
<p class="admonition-title">例题三</p>
@ -2867,7 +2948,7 @@
<p><img alt="根据约束条件剪枝" src="../backtracking_algorithm.assets/preorder_find_constrained_paths.png" /></p>
<p align="center"> Fig. 根据约束条件剪枝 </p>
<h2 id="1213">12.1.3. &nbsp; 常用术语<a class="headerlink" href="#1213" title="Permanent link">&para;</a></h2>
<h2 id="1313">13.1.3. &nbsp; 常用术语<a class="headerlink" href="#1313" title="Permanent link">&para;</a></h2>
<p>为了更清晰地分析算法问题,我们总结一下回溯算法中常用术语的含义,并对照例题三给出对应示例。</p>
<table>
<thead>
@ -2914,7 +2995,7 @@
<p class="admonition-title">Tip</p>
<p>解、状态、约束条件等术语是通用的,适用于回溯算法、动态规划、贪心算法等。</p>
</div>
<h2 id="1214">12.1.4. &nbsp; 框架代码<a class="headerlink" href="#1214" title="Permanent link">&para;</a></h2>
<h2 id="1314">13.1.4. &nbsp; 框架代码<a class="headerlink" href="#1314" title="Permanent link">&para;</a></h2>
<p>回溯算法可用于解决许多搜索问题、约束满足问题和组合优化问题。为提升代码通用性,我们希望将回溯算法的“尝试、回退、剪枝”的主体框架提炼出来。</p>
<p><code>state</code> 为问题的当前状态,<code>choices</code> 表示当前状态下可以做出的选择,则可得到以下回溯算法的框架代码。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="5:11"><input checked="checked" id="__tabbed_5_1" name="__tabbed_5" type="radio" /><input id="__tabbed_5_2" name="__tabbed_5" type="radio" /><input id="__tabbed_5_3" name="__tabbed_5" type="radio" /><input id="__tabbed_5_4" name="__tabbed_5" type="radio" /><input id="__tabbed_5_5" name="__tabbed_5" type="radio" /><input id="__tabbed_5_6" name="__tabbed_5" type="radio" /><input id="__tabbed_5_7" name="__tabbed_5" type="radio" /><input id="__tabbed_5_8" name="__tabbed_5" type="radio" /><input id="__tabbed_5_9" name="__tabbed_5" type="radio" /><input id="__tabbed_5_10" name="__tabbed_5" type="radio" /><input id="__tabbed_5_11" name="__tabbed_5" type="radio" /><div class="tabbed-labels"><label for="__tabbed_5_1">Java</label><label for="__tabbed_5_2">C++</label><label for="__tabbed_5_3">Python</label><label for="__tabbed_5_4">Go</label><label for="__tabbed_5_5">JavaScript</label><label for="__tabbed_5_6">TypeScript</label><label for="__tabbed_5_7">C</label><label for="__tabbed_5_8">C#</label><label for="__tabbed_5_9">Swift</label><label for="__tabbed_5_10">Zig</label><label for="__tabbed_5_11">Dart</label></div>
@ -3590,7 +3671,7 @@
</div>
</div>
<p>相较于基于前序遍历的实现代码,基于回溯算法框架的实现代码虽然显得啰嗦,但通用性更好。实际上,<strong>所有回溯问题都可以在该框架下解决</strong>。我们需要根据具体问题来定义 <code>state</code><code>choices</code> ,并实现框架中的各个方法。</p>
<h2 id="1215">12.1.5. &nbsp; 优势与局限性<a class="headerlink" href="#1215" title="Permanent link">&para;</a></h2>
<h2 id="1315">13.1.5. &nbsp; 优势与局限性<a class="headerlink" href="#1315" title="Permanent link">&para;</a></h2>
<p>回溯算法本质上是一种深度优先搜索算法,它尝试所有可能的解决方案直到找到满足条件的解。这种方法的优势在于它能够找到所有可能的解决方案,而且在合理的剪枝操作下,具有很高的效率。</p>
<p>然而,在处理大规模或者复杂问题时,<strong>回溯算法的运行效率可能难以接受</strong></p>
<ul>
@ -3602,7 +3683,7 @@
<li>上文介绍过的剪枝是一种常用的优化方法。它可以避免搜索那些肯定不会产生有效解的路径,从而节省时间和空间。</li>
<li>另一个常用的优化方法是加入「启发式搜索 Heuristic Search」策略它在搜索过程中引入一些策略或者估计值从而优先搜索最有可能产生有效解的路径。</li>
</ul>
<h2 id="1216">12.1.6. &nbsp; 典型例题<a class="headerlink" href="#1216" title="Permanent link">&para;</a></h2>
<h2 id="1316">13.1.6. &nbsp; 典型例题<a class="headerlink" href="#1316" title="Permanent link">&para;</a></h2>
<p><strong>搜索问题</strong>:这类问题的目标是找到满足特定条件的解决方案。</p>
<ul>
<li>全排列问题:给定一个集合,求出其所有可能的排列组合。</li>
@ -3699,7 +3780,7 @@
<nav class="md-footer__inner md-grid" aria-label="页脚" >
<a href="../" class="md-footer__link md-footer__link--prev" aria-label="上一页: 12. &amp;nbsp; 回溯" rel="prev">
<a href="../" class="md-footer__link md-footer__link--prev" aria-label="上一页: 13. &amp;nbsp; 回溯" rel="prev">
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
</div>
@ -3708,20 +3789,20 @@
上一页
</span>
<div class="md-ellipsis">
12. &nbsp; 回溯
13. &nbsp; 回溯
</div>
</div>
</a>
<a href="../permutations_problem/" class="md-footer__link md-footer__link--next" aria-label="下一页: 12.2. &amp;nbsp; 全排列问题" rel="next">
<a href="../permutations_problem/" class="md-footer__link md-footer__link--next" aria-label="下一页: 13.2. &amp;nbsp; 全排列问题" rel="next">
<div class="md-footer__title">
<span class="md-footer__direction">
下一页
</span>
<div class="md-ellipsis">
12.2. &nbsp; 全排列问题
13.2. &nbsp; 全排列问题
</div>
</div>
<div class="md-footer__button md-icon">