This commit is contained in:
krahets
2023-07-24 03:03:58 +08:00
parent a86a371780
commit 3e2ab6a857
19 changed files with 379 additions and 456 deletions

View File

@@ -1405,26 +1405,6 @@
7.2.1.   层序遍历
</a>
<nav class="md-nav" aria-label="7.2.1.   层序遍历">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#_1" class="md-nav__link">
算法实现
</a>
</li>
<li class="md-nav__item">
<a href="#_2" class="md-nav__link">
复杂度分析
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
@@ -1432,26 +1412,6 @@
7.2.2. &nbsp; 前序、中序、后序遍历
</a>
<nav class="md-nav" aria-label="7.2.2.   前序、中序、后序遍历">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#_3" class="md-nav__link">
算法实现
</a>
</li>
<li class="md-nav__item">
<a href="#_4" class="md-nav__link">
复杂度分析
</a>
</li>
</ul>
</nav>
</li>
</ul>
@@ -3381,26 +3341,6 @@
7.2.1. &nbsp; 层序遍历
</a>
<nav class="md-nav" aria-label="7.2.1.   层序遍历">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#_1" class="md-nav__link">
算法实现
</a>
</li>
<li class="md-nav__item">
<a href="#_2" class="md-nav__link">
复杂度分析
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
@@ -3408,26 +3348,6 @@
7.2.2. &nbsp; 前序、中序、后序遍历
</a>
<nav class="md-nav" aria-label="7.2.2.   前序、中序、后序遍历">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#_3" class="md-nav__link">
算法实现
</a>
</li>
<li class="md-nav__item">
<a href="#_4" class="md-nav__link">
复杂度分析
</a>
</li>
</ul>
</nav>
</li>
</ul>
@@ -3462,7 +3382,6 @@
<p><img alt="二叉树的层序遍历" src="../binary_tree_traversal.assets/binary_tree_bfs.png" /></p>
<p align="center"> Fig. 二叉树的层序遍历 </p>
<h3 id="_1">算法实现<a class="headerlink" href="#_1" title="Permanent link">&para;</a></h3>
<p>广度优先遍历通常借助「队列」来实现。队列遵循“先进先出”的规则,而广度优先遍历则遵循“逐层推进”的规则,两者背后的思想是一致的。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="1:11"><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" /><input id="__tabbed_1_6" name="__tabbed_1" type="radio" /><input id="__tabbed_1_7" name="__tabbed_1" type="radio" /><input id="__tabbed_1_8" name="__tabbed_1" type="radio" /><input id="__tabbed_1_9" name="__tabbed_1" type="radio" /><input id="__tabbed_1_10" name="__tabbed_1" type="radio" /><input id="__tabbed_1_11" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">Java</label><label for="__tabbed_1_2">C++</label><label for="__tabbed_1_3">Python</label><label for="__tabbed_1_4">Go</label><label for="__tabbed_1_5">JavaScript</label><label for="__tabbed_1_6">TypeScript</label><label for="__tabbed_1_7">C</label><label for="__tabbed_1_8">C#</label><label for="__tabbed_1_9">Swift</label><label for="__tabbed_1_10">Zig</label><label for="__tabbed_1_11">Dart</label></div>
<div class="tabbed-content">
@@ -3724,44 +3643,15 @@
</div>
</div>
</div>
<h3 id="_2">复杂度分析<a class="headerlink" href="#_2" title="Permanent link">&para;</a></h3>
<p><strong>时间复杂度</strong>:所有节点被访问一次,使用 <span class="arithmatex">\(O(n)\)</span> 时间,其中 <span class="arithmatex">\(n\)</span> 为节点数量。</p>
<p><strong>空间复杂度</strong>:在最差情况下,即满二叉树时,遍历到最底层之前,队列中最多同时存在 <span class="arithmatex">\(\frac{n + 1}{2}\)</span> 个节点,占用 <span class="arithmatex">\(O(n)\)</span> 空间。</p>
<h2 id="722">7.2.2. &nbsp; 前序、中序、后序遍历<a class="headerlink" href="#722" title="Permanent link">&para;</a></h2>
<p>相应地,前序、中序和后序遍历都属于「深度优先遍历 Depth-First Traversal」它体现了一种“先走到尽头再回溯继续”的遍历方式。</p>
<p>如下图所示,左侧是深度优先遍历的示意图,右上方是对应的递归实现代码。深度优先遍历就像是绕着整个二叉树的外围“走”一圈,在这个过程中,在每个节点都会遇到三个位置,分别对应前序遍历、中序遍历和后序遍历。</p>
<p>如下图所示,左侧是深度优先遍历的示意图,右上方是对应的递归代码。深度优先遍历就像是绕着整个二叉树的外围“走”一圈,在这个过程中,在每个节点都会遇到三个位置,分别对应前序遍历、中序遍历和后序遍历。</p>
<p><img alt="二叉搜索树的前、中、后序遍历" src="../binary_tree_traversal.assets/binary_tree_dfs.png" /></p>
<p align="center"> Fig. 二叉搜索树的前、中、后序遍历 </p>
<div class="center-table">
<table>
<thead>
<tr>
<th>位置</th>
<th>含义</th>
<th>此处访问节点时对应</th>
</tr>
</thead>
<tbody>
<tr>
<td>橙色圆圈处</td>
<td>刚进入此节点,即将访问该节点的左子树</td>
<td>前序遍历 Pre-Order Traversal</td>
</tr>
<tr>
<td>蓝色圆圈处</td>
<td>已访问完左子树,即将访问右子树</td>
<td>中序遍历 In-Order Traversal</td>
</tr>
<tr>
<td>紫色圆圈处</td>
<td>已访问完左子树和右子树,即将返回</td>
<td>后序遍历 Post-Order Traversal</td>
</tr>
</tbody>
</table>
</div>
<h3 id="_3">算法实现<a class="headerlink" href="#_3" title="Permanent link">&para;</a></h3>
<p>以下给出了实现代码,请配合上图理解深度优先遍历的递归过程。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="2:11"><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" /><input id="__tabbed_2_11" name="__tabbed_2" type="radio" /><div class="tabbed-labels"><label for="__tabbed_2_1">Java</label><label for="__tabbed_2_2">C++</label><label for="__tabbed_2_3">Python</label><label for="__tabbed_2_4">Go</label><label for="__tabbed_2_5">JavaScript</label><label for="__tabbed_2_6">TypeScript</label><label for="__tabbed_2_7">C</label><label for="__tabbed_2_8">C#</label><label for="__tabbed_2_9">Swift</label><label for="__tabbed_2_10">Zig</label><label for="__tabbed_2_11">Dart</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -4112,11 +4002,17 @@
</div>
</div>
</div>
<p><strong>时间复杂度</strong>:所有节点被访问一次,使用 <span class="arithmatex">\(O(n)\)</span> 时间,其中 <span class="arithmatex">\(n\)</span> 为节点数量。</p>
<p><strong>空间复杂度</strong>:在最差情况下,即树退化为链表时,递归深度达到 <span class="arithmatex">\(n\)</span> ,系统占用 <span class="arithmatex">\(O(n)\)</span> 栈帧空间。</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>我们也可以仅基于循环实现前、中、后序遍历,有兴趣的同学可以自行实现</p>
<p>我们也可以不使用递归,仅基于迭代实现前、中、后序遍历,有兴趣的同学可以自行研究</p>
</div>
<p>递归过程可分为“递”和“归”两个相反的部分。“递”表示开启新方法,程序在此过程中访问下一个节点;“归”表示函数返回,代表该节点已经访问完毕。如下图所示,为前序遍历二叉树的递归过程。</p>
<p>下图展示了前序遍历二叉树的递归过程,其可分为“递”和“归”两个逆向的部分</p>
<ol>
<li>“递”表示开启新方法,程序在此过程中访问下一个节点。</li>
<li>“归”表示函数返回,代表当前节点已经访问完毕。</li>
</ol>
<div class="tabbed-set tabbed-alternate" data-tabs="3:11"><input checked="checked" id="__tabbed_3_1" name="__tabbed_3" type="radio" /><input id="__tabbed_3_2" name="__tabbed_3" type="radio" /><input id="__tabbed_3_3" name="__tabbed_3" type="radio" /><input id="__tabbed_3_4" name="__tabbed_3" type="radio" /><input id="__tabbed_3_5" name="__tabbed_3" type="radio" /><input id="__tabbed_3_6" name="__tabbed_3" type="radio" /><input id="__tabbed_3_7" name="__tabbed_3" type="radio" /><input id="__tabbed_3_8" name="__tabbed_3" type="radio" /><input id="__tabbed_3_9" name="__tabbed_3" type="radio" /><input id="__tabbed_3_10" name="__tabbed_3" type="radio" /><input id="__tabbed_3_11" name="__tabbed_3" type="radio" /><div class="tabbed-labels"><label for="__tabbed_3_1">&lt;1&gt;</label><label for="__tabbed_3_2">&lt;2&gt;</label><label for="__tabbed_3_3">&lt;3&gt;</label><label for="__tabbed_3_4">&lt;4&gt;</label><label for="__tabbed_3_5">&lt;5&gt;</label><label for="__tabbed_3_6">&lt;6&gt;</label><label for="__tabbed_3_7">&lt;7&gt;</label><label for="__tabbed_3_8">&lt;8&gt;</label><label for="__tabbed_3_9">&lt;9&gt;</label><label for="__tabbed_3_10">&lt;10&gt;</label><label for="__tabbed_3_11">&lt;11&gt;</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -4154,9 +4050,6 @@
</div>
</div>
</div>
<h3 id="_4">复杂度分析<a class="headerlink" href="#_4" title="Permanent link">&para;</a></h3>
<p><strong>时间复杂度</strong>:所有节点被访问一次,使用 <span class="arithmatex">\(O(n)\)</span> 时间,其中 <span class="arithmatex">\(n\)</span> 为节点数量。</p>
<p><strong>空间复杂度</strong>:在最差情况下,即树退化为链表时,递归深度达到 <span class="arithmatex">\(n\)</span> ,系统占用 <span class="arithmatex">\(O(n)\)</span> 栈帧空间。</p>