This commit is contained in:
krahets
2023-07-17 02:17:51 +08:00
parent dbf682ebc9
commit 7686588be4
121 changed files with 4804 additions and 233 deletions
@@ -12,13 +12,13 @@
<meta name="author" content="Krahets">
<link rel="canonical" href="https://www.hello-algo.com/chapter_divide_and_conquer/build_binary_tree/">
<link rel="canonical" href="https://www.hello-algo.com/chapter_divide_and_conquer/build_binary_tree_problem/">
<link rel="prev" href="../divide_and_conquer/">
<link rel="next" href="../../chapter_backtracking/">
<link rel="next" href="../hanota_problem/">
<link rel="icon" href="../../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.4.2, mkdocs-material-9.1.11">
@@ -1773,6 +1773,8 @@
@@ -1833,6 +1835,20 @@
<li class="md-nav__item">
<a href="../hanota_problem/" class="md-nav__link">
12.3. &nbsp; 汉诺塔问题(New
</a>
</li>
</ul>
</nav>
</li>
@@ -2276,7 +2292,7 @@
<a href="https://github.com/krahets/hello-algo/tree/main/docs/chapter_divide_and_conquer/build_binary_tree.md" title="编辑此页" class="md-content__button md-icon">
<a href="https://github.com/krahets/hello-algo/tree/main/docs/chapter_divide_and_conquer/build_binary_tree_problem.md" title="编辑此页" class="md-content__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 20H6V4h7v5h5v3.1l2-2V8l-6-6H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4v-2m10.2-7c.1 0 .3.1.4.2l1.3 1.3c.2.2.2.6 0 .8l-1 1-2.1-2.1 1-1c.1-.1.2-.2.4-.2m0 3.9L14.1 23H12v-2.1l6.1-6.1 2.1 2.1Z"/></svg>
</a>
@@ -2289,7 +2305,7 @@
<p class="admonition-title">Question</p>
<p>给定一个二叉树的前序遍历 <code>preorder</code> 和中序遍历 <code>inorder</code> ,请从中构建二叉树,返回二叉树的根节点。</p>
</div>
<p><img alt="构建二叉树的示例数据" src="../build_binary_tree.assets/build_tree_example.png" /></p>
<p><img alt="构建二叉树的示例数据" src="../build_binary_tree_problem.assets/build_tree_example.png" /></p>
<p align="center"> Fig. 构建二叉树的示例数据 </p>
<p>原问题定义为从 <code>preorder</code><code>inorder</code> 构建二叉树。我们首先从分治的角度分析这道题:</p>
@@ -2310,7 +2326,7 @@
<li>查找根节点在 <code>inorder</code> 中的索引,基于该索引可将 <code>inorder</code> 划分为 <code>[ 9 | 3 1 2 7 ]</code> </li>
<li>根据 <code>inorder</code> 划分结果,可得左子树和右子树分别有 1 个和 3 个节点,从而可将 <code>preorder</code> 划分为 <code>[ 3 | 9 | 2 1 7 ]</code> </li>
</ol>
<p><img alt="在前序和中序遍历中划分子树" src="../build_binary_tree.assets/build_tree_preorder_inorder_division.png" /></p>
<p><img alt="在前序和中序遍历中划分子树" src="../build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png" /></p>
<p align="center"> Fig. 在前序和中序遍历中划分子树 </p>
<p>至此,<strong>我们已经推导出根节点、左子树、右子树在 <code>preorder</code><code>inorder</code> 中的索引区间</strong>。而为了描述这些索引区间,我们需要借助几个指针变量:</p>
@@ -2349,7 +2365,7 @@
</table>
</div>
<p>请注意,右子树根节点索引中的 <span class="arithmatex">\((m-l)\)</span> 的含义是“左子树的节点数量”,建议配合下图理解。</p>
<p><img alt="根节点和左右子树的索引区间表示" src="../build_binary_tree.assets/build_tree_division_pointers.png" /></p>
<p><img alt="根节点和左右子树的索引区间表示" src="../build_binary_tree_problem.assets/build_tree_division_pointers.png" /></p>
<p align="center"> Fig. 根节点和左右子树的索引区间表示 </p>
<p>接下来就可以实现代码了。为了提升查询 <span class="arithmatex">\(m\)</span> 的效率,我们借助一个哈希表 <code>hmap</code> 来存储 <code>inorder</code> 列表元素到索引的映射。</p>
@@ -2376,7 +2392,7 @@
<a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a> <span class="n">l</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
<a id="__codelineno-2-7" name="__codelineno-2-7" href="#__codelineno-2-7"></a> <span class="n">r</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
<a id="__codelineno-2-8" name="__codelineno-2-8" href="#__codelineno-2-8"></a><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">TreeNode</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
<a id="__codelineno-2-9" name="__codelineno-2-9" href="#__codelineno-2-9"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;构建二叉树 DFS&quot;&quot;&quot;</span>
<a id="__codelineno-2-9" name="__codelineno-2-9" href="#__codelineno-2-9"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;构建二叉树:分治&quot;&quot;&quot;</span>
<a id="__codelineno-2-10" name="__codelineno-2-10" href="#__codelineno-2-10"></a> <span class="c1"># 子树区间为空时终止</span>
<a id="__codelineno-2-11" name="__codelineno-2-11" href="#__codelineno-2-11"></a> <span class="k">if</span> <span class="n">r</span> <span class="o">-</span> <span class="n">l</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">:</span>
<a id="__codelineno-2-12" name="__codelineno-2-12" href="#__codelineno-2-12"></a> <span class="k">return</span> <span class="kc">None</span>
@@ -2384,9 +2400,9 @@
<a id="__codelineno-2-14" name="__codelineno-2-14" href="#__codelineno-2-14"></a> <span class="n">root</span> <span class="o">=</span> <span class="n">TreeNode</span><span class="p">(</span><span class="n">preorder</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
<a id="__codelineno-2-15" name="__codelineno-2-15" href="#__codelineno-2-15"></a> <span class="c1"># 查询 m ,从而划分左右子树</span>
<a id="__codelineno-2-16" name="__codelineno-2-16" href="#__codelineno-2-16"></a> <span class="n">m</span> <span class="o">=</span> <span class="n">hmap</span><span class="p">[</span><span class="n">preorder</span><span class="p">[</span><span class="n">i</span><span class="p">]]</span>
<a id="__codelineno-2-17" name="__codelineno-2-17" href="#__codelineno-2-17"></a> <span class="c1"># 递归构建左子树</span>
<a id="__codelineno-2-17" name="__codelineno-2-17" href="#__codelineno-2-17"></a> <span class="c1"># 子问题:构建左子树</span>
<a id="__codelineno-2-18" name="__codelineno-2-18" href="#__codelineno-2-18"></a> <span class="n">root</span><span class="o">.</span><span class="n">left</span> <span class="o">=</span> <span class="n">dfs</span><span class="p">(</span><span class="n">preorder</span><span class="p">,</span> <span class="n">inorder</span><span class="p">,</span> <span class="n">hmap</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">l</span><span class="p">,</span> <span class="n">m</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
<a id="__codelineno-2-19" name="__codelineno-2-19" href="#__codelineno-2-19"></a> <span class="c1"># 递归构建右子树</span>
<a id="__codelineno-2-19" name="__codelineno-2-19" href="#__codelineno-2-19"></a> <span class="c1"># 子问题:构建右子树</span>
<a id="__codelineno-2-20" name="__codelineno-2-20" href="#__codelineno-2-20"></a> <span class="n">root</span><span class="o">.</span><span class="n">right</span> <span class="o">=</span> <span class="n">dfs</span><span class="p">(</span><span class="n">preorder</span><span class="p">,</span> <span class="n">inorder</span><span class="p">,</span> <span class="n">hmap</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">m</span> <span class="o">-</span> <span class="n">l</span><span class="p">,</span> <span class="n">m</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span>
<a id="__codelineno-2-21" name="__codelineno-2-21" href="#__codelineno-2-21"></a> <span class="c1"># 返回根节点</span>
<a id="__codelineno-2-22" name="__codelineno-2-22" href="#__codelineno-2-22"></a> <span class="k">return</span> <span class="n">root</span>
@@ -2453,34 +2469,34 @@
<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-content">
<div class="tabbed-block">
<p><img alt="built_tree_step1" src="../build_binary_tree.assets/built_tree_step1.png" /></p>
<p><img alt="构建二叉树的递归过程" src="../build_binary_tree_problem.assets/built_tree_step1.png" /></p>
</div>
<div class="tabbed-block">
<p><img alt="built_tree_step2" src="../build_binary_tree.assets/built_tree_step2.png" /></p>
<p><img alt="built_tree_step2" src="../build_binary_tree_problem.assets/built_tree_step2.png" /></p>
</div>
<div class="tabbed-block">
<p><img alt="built_tree_step3" src="../build_binary_tree.assets/built_tree_step3.png" /></p>
<p><img alt="built_tree_step3" src="../build_binary_tree_problem.assets/built_tree_step3.png" /></p>
</div>
<div class="tabbed-block">
<p><img alt="built_tree_step4" src="../build_binary_tree.assets/built_tree_step4.png" /></p>
<p><img alt="built_tree_step4" src="../build_binary_tree_problem.assets/built_tree_step4.png" /></p>
</div>
<div class="tabbed-block">
<p><img alt="built_tree_step5" src="../build_binary_tree.assets/built_tree_step5.png" /></p>
<p><img alt="built_tree_step5" src="../build_binary_tree_problem.assets/built_tree_step5.png" /></p>
</div>
<div class="tabbed-block">
<p><img alt="built_tree_step6" src="../build_binary_tree.assets/built_tree_step6.png" /></p>
<p><img alt="built_tree_step6" src="../build_binary_tree_problem.assets/built_tree_step6.png" /></p>
</div>
<div class="tabbed-block">
<p><img alt="built_tree_step7" src="../build_binary_tree.assets/built_tree_step7.png" /></p>
<p><img alt="built_tree_step7" src="../build_binary_tree_problem.assets/built_tree_step7.png" /></p>
</div>
<div class="tabbed-block">
<p><img alt="built_tree_step8" src="../build_binary_tree.assets/built_tree_step8.png" /></p>
<p><img alt="built_tree_step8" src="../build_binary_tree_problem.assets/built_tree_step8.png" /></p>
</div>
<div class="tabbed-block">
<p><img alt="built_tree_step9" src="../build_binary_tree.assets/built_tree_step9.png" /></p>
<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.assets/built_tree_step10.png" /></p>
<p><img alt="built_tree_step10" src="../build_binary_tree_problem.assets/built_tree_step10.png" /></p>
</div>
</div>
</div>
@@ -2579,13 +2595,13 @@
<a href="../../chapter_backtracking/" class="md-footer__link md-footer__link--next" aria-label="下一页: 13. &amp;nbsp; 回溯" rel="next">
<a href="../hanota_problem/" class="md-footer__link md-footer__link--next" aria-label="下一页: 12.3. &amp;nbsp; 汉诺塔问题(New" rel="next">
<div class="md-footer__title">
<span class="md-footer__direction">
下一页
</span>
<div class="md-ellipsis">
13. &nbsp; 回溯
12.3. &nbsp; 汉诺塔问题(New
</div>
</div>
<div class="md-footer__button md-icon">
@@ -18,7 +18,7 @@
<link rel="prev" href="../">
<link rel="next" href="../build_binary_tree/">
<link rel="next" href="../build_binary_tree_problem/">
<link rel="icon" href="../../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.4.2, mkdocs-material-9.1.11">
@@ -1773,6 +1773,8 @@
@@ -1883,7 +1885,7 @@
<li class="md-nav__item">
<a href="../build_binary_tree/" class="md-nav__link">
<a href="../build_binary_tree_problem/" class="md-nav__link">
12.2. &nbsp; 构建树问题(New
</a>
</li>
@@ -1891,6 +1893,20 @@
<li class="md-nav__item">
<a href="../hanota_problem/" class="md-nav__link">
12.3. &nbsp; 汉诺塔问题(New
</a>
</li>
</ul>
</nav>
</li>
@@ -2384,7 +2400,7 @@
<h1 id="121">12.1. &nbsp; 分治<a class="headerlink" href="#121" title="Permanent link">&para;</a></h1>
<h1 id="121">12.1. &nbsp; 分治算法<a class="headerlink" href="#121" title="Permanent link">&para;</a></h1>
<p>「分治 Divide and Conquer」,全称分而治之,是一种非常重要的算法策略。分治通常基于递归实现,包括“分”和“治”两部分,主要步骤如下:</p>
<ol>
<li><strong>分(划分阶段)</strong>:递归地将原问题分解为两个或多个子问题,直至到达最小子问题时终止;</li>
@@ -2552,7 +2568,7 @@ n(n - 4) &amp; &gt; 0
<a href="../build_binary_tree/" class="md-footer__link md-footer__link--next" aria-label="下一页: 12.2. &amp;nbsp; 构建树问题(New" rel="next">
<a href="../build_binary_tree_problem/" class="md-footer__link md-footer__link--next" aria-label="下一页: 12.2. &amp;nbsp; 构建树问题(New" rel="next">
<div class="md-footer__title">
<span class="md-footer__direction">
下一页
Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

File diff suppressed because it is too large Load Diff
+17 -1
View File
@@ -1773,6 +1773,8 @@
@@ -1815,7 +1817,7 @@
<li class="md-nav__item">
<a href="build_binary_tree/" class="md-nav__link">
<a href="build_binary_tree_problem/" class="md-nav__link">
12.2. &nbsp; 构建树问题(New
</a>
</li>
@@ -1823,6 +1825,20 @@
<li class="md-nav__item">
<a href="hanota_problem/" class="md-nav__link">
12.3. &nbsp; 汉诺塔问题(New
</a>
</li>
</ul>
</nav>
</li>