This commit is contained in:
krahets 2023-08-30 02:52:40 +08:00
parent 8c4d24795c
commit 89d7b7f71f
14 changed files with 23 additions and 22 deletions

View File

@ -3861,7 +3861,7 @@
</div>
</div>
<p>图 12-8 展示了构建二叉树的递归过程,各个节点是在向下“递”的过程中建立的,而各条边(即引用)是在向上“归”的过程中建立的。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="2:10"><input checked="checked" id="__tabbed_2_1" name="__tabbed_2" type="radio" /><input id="__tabbed_2_2" name="__tabbed_2" type="radio" /><input id="__tabbed_2_3" name="__tabbed_2" type="radio" /><input id="__tabbed_2_4" name="__tabbed_2" type="radio" /><input id="__tabbed_2_5" name="__tabbed_2" type="radio" /><input id="__tabbed_2_6" name="__tabbed_2" type="radio" /><input id="__tabbed_2_7" name="__tabbed_2" type="radio" /><input id="__tabbed_2_8" name="__tabbed_2" type="radio" /><input id="__tabbed_2_9" name="__tabbed_2" type="radio" /><input id="__tabbed_2_10" name="__tabbed_2" type="radio" /><div class="tabbed-labels"><label for="__tabbed_2_1">&lt;1&gt;</label><label for="__tabbed_2_2">&lt;2&gt;</label><label for="__tabbed_2_3">&lt;3&gt;</label><label for="__tabbed_2_4">&lt;4&gt;</label><label for="__tabbed_2_5">&lt;5&gt;</label><label for="__tabbed_2_6">&lt;6&gt;</label><label for="__tabbed_2_7">&lt;7&gt;</label><label for="__tabbed_2_8">&lt;8&gt;</label><label for="__tabbed_2_9">&lt;9&gt;</label><label for="__tabbed_2_10">&lt;10&gt;</label></div>
<div class="tabbed-set tabbed-alternate" data-tabs="2:9"><input checked="checked" id="__tabbed_2_1" name="__tabbed_2" type="radio" /><input id="__tabbed_2_2" name="__tabbed_2" type="radio" /><input id="__tabbed_2_3" name="__tabbed_2" type="radio" /><input id="__tabbed_2_4" name="__tabbed_2" type="radio" /><input id="__tabbed_2_5" name="__tabbed_2" type="radio" /><input id="__tabbed_2_6" name="__tabbed_2" type="radio" /><input id="__tabbed_2_7" name="__tabbed_2" type="radio" /><input id="__tabbed_2_8" name="__tabbed_2" type="radio" /><input id="__tabbed_2_9" name="__tabbed_2" type="radio" /><div class="tabbed-labels"><label for="__tabbed_2_1">&lt;1&gt;</label><label for="__tabbed_2_2">&lt;2&gt;</label><label for="__tabbed_2_3">&lt;3&gt;</label><label for="__tabbed_2_4">&lt;4&gt;</label><label for="__tabbed_2_5">&lt;5&gt;</label><label for="__tabbed_2_6">&lt;6&gt;</label><label for="__tabbed_2_7">&lt;7&gt;</label><label for="__tabbed_2_8">&lt;8&gt;</label><label for="__tabbed_2_9">&lt;9&gt;</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
<p><img alt="构建二叉树的递归过程" src="../build_binary_tree_problem.assets/built_tree_step1.png" /></p>
@ -3890,13 +3890,14 @@
<div class="tabbed-block">
<p><img alt="built_tree_step9" src="../build_binary_tree_problem.assets/built_tree_step9.png" /></p>
</div>
<div class="tabbed-block">
<p><img alt="built_tree_step10" src="../build_binary_tree_problem.assets/built_tree_step10.png" /></p>
</div>
</div>
</div>
<p align="center"> 图 12-8 &nbsp; 构建二叉树的递归过程 </p>
<p>每个递归函数内的前序遍历 <code>preorder</code> 和中序遍历 <code>inorder</code> 的划分结果如图 12-9 所示。</p>
<p><img alt="built_tree_overall" src="../build_binary_tree_problem.assets/built_tree_overall.png" /></p>
<p align="center"> 图 12-9 &nbsp; built_tree_overall </p>
<p>设树的节点数量为 <span class="arithmatex">\(n\)</span> ,初始化每一个节点(执行一个递归函数 <code>dfs()</code> )使用 <span class="arithmatex">\(O(1)\)</span> 时间。<strong>因此总体时间复杂度为 <span class="arithmatex">\(O(n)\)</span></strong></p>
<p>哈希表存储 <code>inorder</code> 元素到索引的映射,空间复杂度为 <span class="arithmatex">\(O(n)\)</span> 。最差情况下,即二叉树退化为链表时,递归深度达到 <span class="arithmatex">\(n\)</span> ,使用 <span class="arithmatex">\(O(n)\)</span> 的栈帧空间。<strong>因此总体空间复杂度为 <span class="arithmatex">\(O(n)\)</span></strong></p>

View File

@ -3475,11 +3475,11 @@
</ol>
</div>
<p><img alt="汉诺塔问题示例" src="../hanota_problem.assets/hanota_example.png" /></p>
<p align="center"> 图 12-9 &nbsp; 汉诺塔问题示例 </p>
<p align="center"> 图 12-10 &nbsp; 汉诺塔问题示例 </p>
<p><strong>我们将规模为 <span class="arithmatex">\(i\)</span> 的汉诺塔问题记做 <span class="arithmatex">\(f(i)\)</span></strong> 。例如 <span class="arithmatex">\(f(3)\)</span> 代表将 <span class="arithmatex">\(3\)</span> 个圆盘从 <code>A</code> 移动至 <code>C</code> 的汉诺塔问题。</p>
<h3 id="1">1. &nbsp; 考虑基本情况<a class="headerlink" href="#1" title="Permanent link">&para;</a></h3>
<p>如图 12-10 所示,对于问题 <span class="arithmatex">\(f(1)\)</span> ,即当只有一个圆盘时,我们将它直接从 <code>A</code> 移动至 <code>C</code> 即可。</p>
<p>如图 12-11 所示,对于问题 <span class="arithmatex">\(f(1)\)</span> ,即当只有一个圆盘时,我们将它直接从 <code>A</code> 移动至 <code>C</code> 即可。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="1:2"><input checked="checked" id="__tabbed_1_1" name="__tabbed_1" type="radio" /><input id="__tabbed_1_2" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">&lt;1&gt;</label><label for="__tabbed_1_2">&lt;2&gt;</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
@ -3490,9 +3490,9 @@
</div>
</div>
</div>
<p align="center"> 图 12-10 &nbsp; 规模为 1 问题的解 </p>
<p align="center"> 图 12-11 &nbsp; 规模为 1 问题的解 </p>
<p>如图 12-11 所示,对于问题 <span class="arithmatex">\(f(2)\)</span> ,即当有两个圆盘时,<strong>由于要时刻满足小圆盘在大圆盘之上,因此需要借助 <code>B</code> 来完成移动</strong></p>
<p>如图 12-12 所示,对于问题 <span class="arithmatex">\(f(2)\)</span> ,即当有两个圆盘时,<strong>由于要时刻满足小圆盘在大圆盘之上,因此需要借助 <code>B</code> 来完成移动</strong></p>
<ol>
<li>先将上面的小圆盘从 <code>A</code> 移至 <code>B</code></li>
<li>再将大圆盘从 <code>A</code> 移至 <code>C</code></li>
@ -3514,12 +3514,12 @@
</div>
</div>
</div>
<p align="center"> 图 12-11 &nbsp; 规模为 2 问题的解 </p>
<p align="center"> 图 12-12 &nbsp; 规模为 2 问题的解 </p>
<p>解决问题 <span class="arithmatex">\(f(2)\)</span> 的过程可总结为:<strong>将两个圆盘借助 <code>B</code><code>A</code> 移至 <code>C</code></strong> 。其中,<code>C</code> 称为目标柱、<code>B</code> 称为缓冲柱。</p>
<h3 id="2">2. &nbsp; 子问题分解<a class="headerlink" href="#2" title="Permanent link">&para;</a></h3>
<p>对于问题 <span class="arithmatex">\(f(3)\)</span> ,即当有三个圆盘时,情况变得稍微复杂了一些。</p>
<p>因为已知 <span class="arithmatex">\(f(1)\)</span><span class="arithmatex">\(f(2)\)</span> 的解,所以我们可从分治角度思考,<strong><code>A</code> 顶部的两个圆盘看做一个整体</strong>,执行图 12-12 所示的步骤。这样三个圆盘就被顺利地从 <code>A</code> 移动至 <code>C</code> 了。</p>
<p>因为已知 <span class="arithmatex">\(f(1)\)</span><span class="arithmatex">\(f(2)\)</span> 的解,所以我们可从分治角度思考,<strong><code>A</code> 顶部的两个圆盘看做一个整体</strong>,执行图 12-13 所示的步骤。这样三个圆盘就被顺利地从 <code>A</code> 移动至 <code>C</code> 了。</p>
<ol>
<li><code>B</code> 为目标柱、<code>C</code> 为缓冲柱,将两个圆盘从 <code>A</code> 移动至 <code>B</code></li>
<li><code>A</code> 中剩余的一个圆盘从 <code>A</code> 直接移动至 <code>C</code></li>
@ -3541,10 +3541,10 @@
</div>
</div>
</div>
<p align="center"> 图 12-12 &nbsp; 规模为 3 问题的解 </p>
<p align="center"> 图 12-13 &nbsp; 规模为 3 问题的解 </p>
<p>本质上看,<strong>我们将问题 <span class="arithmatex">\(f(3)\)</span> 划分为两个子问题 <span class="arithmatex">\(f(2)\)</span> 和子问题 <span class="arithmatex">\(f(1)\)</span></strong> 。按顺序解决这三个子问题之后,原问题随之得到解决。这说明子问题是独立的,而且解是可以合并的。</p>
<p>至此,我们可总结出图 12-13 所示的汉诺塔问题的分治策略:将原问题 <span class="arithmatex">\(f(n)\)</span> 划分为两个子问题 <span class="arithmatex">\(f(n-1)\)</span> 和一个子问题 <span class="arithmatex">\(f(1)\)</span> ,并按照以下顺序解决这三个子问题。</p>
<p>至此,我们可总结出图 12-14 所示的汉诺塔问题的分治策略:将原问题 <span class="arithmatex">\(f(n)\)</span> 划分为两个子问题 <span class="arithmatex">\(f(n-1)\)</span> 和一个子问题 <span class="arithmatex">\(f(1)\)</span> ,并按照以下顺序解决这三个子问题。</p>
<ol>
<li><span class="arithmatex">\(n-1\)</span> 个圆盘借助 <code>C</code><code>A</code> 移至 <code>B</code></li>
<li>将剩余 <span class="arithmatex">\(1\)</span> 个圆盘从 <code>A</code> 直接移至 <code>C</code></li>
@ -3552,7 +3552,7 @@
</ol>
<p>对于这两个子问题 <span class="arithmatex">\(f(n-1)\)</span> <strong>可以通过相同的方式进行递归划分</strong>,直至达到最小子问题 <span class="arithmatex">\(f(1)\)</span> 。而 <span class="arithmatex">\(f(1)\)</span> 的解是已知的,只需一次移动操作即可。</p>
<p><img alt="汉诺塔问题的分治策略" src="../hanota_problem.assets/hanota_divide_and_conquer.png" /></p>
<p align="center"> 图 12-13 &nbsp; 汉诺塔问题的分治策略 </p>
<p align="center"> 图 12-14 &nbsp; 汉诺塔问题的分治策略 </p>
<h3 id="3">3. &nbsp; 代码实现<a class="headerlink" href="#3" title="Permanent link">&para;</a></h3>
<p>在代码中,我们声明一个递归函数 <code>dfs(i, src, buf, tar)</code> ,它的作用是将柱 <code>src</code> 顶部的 <span class="arithmatex">\(i\)</span> 个圆盘借助缓冲柱 <code>buf</code> 移动至目标柱 <code>tar</code></p>
@ -3872,9 +3872,9 @@
</div>
</div>
</div>
<p>如图 12-14 所示,汉诺塔问题形成一个高度为 <span class="arithmatex">\(n\)</span> 的递归树,每个节点代表一个子问题、对应一个开启的 <code>dfs()</code> 函数,<strong>因此时间复杂度为 <span class="arithmatex">\(O(2^n)\)</span> ,空间复杂度为 <span class="arithmatex">\(O(n)\)</span></strong></p>
<p>如图 12-15 所示,汉诺塔问题形成一个高度为 <span class="arithmatex">\(n\)</span> 的递归树,每个节点代表一个子问题、对应一个开启的 <code>dfs()</code> 函数,<strong>因此时间复杂度为 <span class="arithmatex">\(O(2^n)\)</span> ,空间复杂度为 <span class="arithmatex">\(O(n)\)</span></strong></p>
<p><img alt="汉诺塔问题的递归树" src="../hanota_problem.assets/hanota_recursive_tree.png" /></p>
<p align="center"> 图 12-14 &nbsp; 汉诺塔问题的递归树 </p>
<p align="center"> 图 12-15 &nbsp; 汉诺塔问题的递归树 </p>
<div class="admonition quote">
<p class="admonition-title">Quote</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

View File

@ -3497,7 +3497,7 @@ n &amp; \geq 4
<p>接下来思考哪个因子是最优的。在 <span class="arithmatex">\(1\)</span><span class="arithmatex">\(2\)</span><span class="arithmatex">\(3\)</span> 这三个因子中,显然 <span class="arithmatex">\(1\)</span> 是最差的,因为 <span class="arithmatex">\(1 \times (n-1) &lt; n\)</span> 恒成立,即切分出 <span class="arithmatex">\(1\)</span> 反而会导致乘积减小。</p>
<p>如图 15-15 所示,当 <span class="arithmatex">\(n = 6\)</span> 时,有 <span class="arithmatex">\(3 \times 3 &gt; 2 \times 2 \times 2\)</span><strong>这意味着切分出 <span class="arithmatex">\(3\)</span> 比切分出 <span class="arithmatex">\(2\)</span> 更优</strong></p>
<p><strong>贪心策略二</strong>:在切分方案中,最多只应存在两个 <span class="arithmatex">\(2\)</span> 。因为三个 <span class="arithmatex">\(2\)</span> 总是可以被替换为两个 <span class="arithmatex">\(3\)</span> ,从而获得更大乘积。</p>
<p><img alt="最优切分因子" src="../max_product_cutting_problem.assets/max_product_cutting_greedy_infer3.png" /></p>
<p><img alt="最优切分因子" src="../max_product_cutting_problem.assets/max_product_cutting_greedy_infer2.png" /></p>
<p align="center"> 图 15-15 &nbsp; 最优切分因子 </p>
<p>总结以上,可推出以下贪心策略。</p>

View File

@ -3387,19 +3387,19 @@
<div class="tabbed-set tabbed-alternate" data-tabs="1:5"><input checked="checked" id="__tabbed_1_1" name="__tabbed_1" type="radio" /><input id="__tabbed_1_2" name="__tabbed_1" type="radio" /><input id="__tabbed_1_3" name="__tabbed_1" type="radio" /><input id="__tabbed_1_4" name="__tabbed_1" type="radio" /><input id="__tabbed_1_5" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">&lt;1&gt;</label><label for="__tabbed_1_2">&lt;2&gt;</label><label for="__tabbed_1_3">&lt;3&gt;</label><label for="__tabbed_1_4">&lt;4&gt;</label><label for="__tabbed_1_5">&lt;5&gt;</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
<p><img alt="查字典步骤" src="../algorithms_are_everywhere.assets/binary_search_dictionary_step_1.png" /></p>
<p><img alt="查字典步骤" src="../algorithms_are_everywhere.assets/binary_search_dictionary_step1.png" /></p>
</div>
<div class="tabbed-block">
<p><img alt="binary_search_dictionary_step_2" src="../algorithms_are_everywhere.assets/binary_search_dictionary_step_2.png" /></p>
<p><img alt="binary_search_dictionary_step2" src="../algorithms_are_everywhere.assets/binary_search_dictionary_step2.png" /></p>
</div>
<div class="tabbed-block">
<p><img alt="binary_search_dictionary_step_3" src="../algorithms_are_everywhere.assets/binary_search_dictionary_step_3.png" /></p>
<p><img alt="binary_search_dictionary_step3" src="../algorithms_are_everywhere.assets/binary_search_dictionary_step3.png" /></p>
</div>
<div class="tabbed-block">
<p><img alt="binary_search_dictionary_step_4" src="../algorithms_are_everywhere.assets/binary_search_dictionary_step_4.png" /></p>
<p><img alt="binary_search_dictionary_step4" src="../algorithms_are_everywhere.assets/binary_search_dictionary_step4.png" /></p>
</div>
<div class="tabbed-block">
<p><img alt="binary_search_dictionary_step_5" src="../algorithms_are_everywhere.assets/binary_search_dictionary_step_5.png" /></p>
<p><img alt="binary_search_dictionary_step5" src="../algorithms_are_everywhere.assets/binary_search_dictionary_step5.png" /></p>
</div>
</div>
</div>

File diff suppressed because one or more lines are too long

Binary file not shown.