mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-17 06:00:58 +00:00
deploy
This commit is contained in:
@@ -3539,8 +3539,8 @@
|
||||
<h2 id="823">8.2.3. 复杂度分析<a class="headerlink" href="#823" title="Permanent link">¶</a></h2>
|
||||
<p>为什么第二种建堆方法的时间复杂度是 <span class="arithmatex">\(O(n)\)</span> ?我们来展开推算一下。</p>
|
||||
<ul>
|
||||
<li>完全二叉树中,设节点总数为 <span class="arithmatex">\(n\)</span> ,则叶节点数量为 <span class="arithmatex">\((n + 1) / 2\)</span> ,其中 <span class="arithmatex">\(/\)</span> 为向下整除。因此,在排除叶节点后,需要堆化的节点数量为 <span class="arithmatex">\((n - 1)/2\)</span> ,复杂度为 <span class="arithmatex">\(O(n)\)</span> ;</li>
|
||||
<li>在从顶至底堆化的过程中,每个节点最多堆化到叶节点,因此最大迭代次数为二叉树高度 <span class="arithmatex">\(O(\log n)\)</span> ;</li>
|
||||
<li>完全二叉树中,设节点总数为 <span class="arithmatex">\(n\)</span> ,则叶节点数量为 <span class="arithmatex">\((n + 1) / 2\)</span> ,其中 <span class="arithmatex">\(/\)</span> 为向下整除。因此,在排除叶节点后,需要堆化的节点数量为 <span class="arithmatex">\((n - 1)/2\)</span> ,复杂度为 <span class="arithmatex">\(O(n)\)</span> 。</li>
|
||||
<li>在从顶至底堆化的过程中,每个节点最多堆化到叶节点,因此最大迭代次数为二叉树高度 <span class="arithmatex">\(O(\log n)\)</span> 。</li>
|
||||
</ul>
|
||||
<p>将上述两者相乘,可得到建堆过程的时间复杂度为 <span class="arithmatex">\(O(n \log n)\)</span> 。<strong>然而,这个估算结果并不准确,因为我们没有考虑到二叉树底层节点数量远多于顶层节点的特性</strong>。</p>
|
||||
<p>接下来我们来进行更为详细的计算。为了减小计算难度,我们假设树是一个“完美二叉树”,该假设不会影响计算结果的正确性。设二叉树(即堆)节点数量为 <span class="arithmatex">\(n\)</span> ,树高度为 <span class="arithmatex">\(h\)</span> 。上文提到,<strong>节点堆化最大迭代次数等于该节点到叶节点的距离,而该距离正是“节点高度”</strong>。</p>
|
||||
|
||||
@@ -3458,8 +3458,8 @@
|
||||
<h1 id="81">8.1. 堆<a class="headerlink" href="#81" title="Permanent link">¶</a></h1>
|
||||
<p>「堆 Heap」是一种满足特定条件的完全二叉树,可分为两种类型:</p>
|
||||
<ul>
|
||||
<li>「大顶堆 Max Heap」,任意节点的值 <span class="arithmatex">\(\geq\)</span> 其子节点的值;</li>
|
||||
<li>「小顶堆 Min Heap」,任意节点的值 <span class="arithmatex">\(\leq\)</span> 其子节点的值;</li>
|
||||
<li>「大顶堆 Max Heap」,任意节点的值 <span class="arithmatex">\(\geq\)</span> 其子节点的值。</li>
|
||||
<li>「小顶堆 Min Heap」,任意节点的值 <span class="arithmatex">\(\leq\)</span> 其子节点的值。</li>
|
||||
</ul>
|
||||
<p><img alt="小顶堆与大顶堆" src="../heap.assets/min_heap_and_max_heap.png" /></p>
|
||||
<p align="center"> Fig. 小顶堆与大顶堆 </p>
|
||||
@@ -4359,9 +4359,9 @@
|
||||
<h3 id="_4">堆顶元素出堆<a class="headerlink" href="#_4" title="Permanent link">¶</a></h3>
|
||||
<p>堆顶元素是二叉树的根节点,即列表首元素。如果我们直接从列表中删除首元素,那么二叉树中所有节点的索引都会发生变化,这将使得后续使用堆化修复变得困难。为了尽量减少元素索引的变动,我们采取以下操作步骤:</p>
|
||||
<ol>
|
||||
<li>交换堆顶元素与堆底元素(即交换根节点与最右叶节点);</li>
|
||||
<li>交换完成后,将堆底从列表中删除(注意,由于已经交换,实际上删除的是原来的堆顶元素);</li>
|
||||
<li>从根节点开始,<strong>从顶至底执行堆化</strong>;</li>
|
||||
<li>交换堆顶元素与堆底元素(即交换根节点与最右叶节点)。</li>
|
||||
<li>交换完成后,将堆底从列表中删除(注意,由于已经交换,实际上删除的是原来的堆顶元素)。</li>
|
||||
<li>从根节点开始,<strong>从顶至底执行堆化</strong>。</li>
|
||||
</ol>
|
||||
<p>顾名思义,<strong>从顶至底堆化的操作方向与从底至顶堆化相反</strong>,我们将根节点的值与其两个子节点的值进行比较,将最大的子节点与根节点交换;然后循环执行此操作,直到越过叶节点或遇到无需交换的节点时结束。</p>
|
||||
<div class="tabbed-set tabbed-alternate" data-tabs="6:10"><input checked="checked" id="__tabbed_6_1" name="__tabbed_6" type="radio" /><input id="__tabbed_6_2" name="__tabbed_6" type="radio" /><input id="__tabbed_6_3" name="__tabbed_6" type="radio" /><input id="__tabbed_6_4" name="__tabbed_6" type="radio" /><input id="__tabbed_6_5" name="__tabbed_6" type="radio" /><input id="__tabbed_6_6" name="__tabbed_6" type="radio" /><input id="__tabbed_6_7" name="__tabbed_6" type="radio" /><input id="__tabbed_6_8" name="__tabbed_6" type="radio" /><input id="__tabbed_6_9" name="__tabbed_6" type="radio" /><input id="__tabbed_6_10" name="__tabbed_6" type="radio" /><div class="tabbed-labels"><label for="__tabbed_6_1"><1></label><label for="__tabbed_6_2"><2></label><label for="__tabbed_6_3"><3></label><label for="__tabbed_6_4"><4></label><label for="__tabbed_6_5"><5></label><label for="__tabbed_6_6"><6></label><label for="__tabbed_6_7"><7></label><label for="__tabbed_6_8"><8></label><label for="__tabbed_6_9"><9></label><label for="__tabbed_6_10"><10></label></div>
|
||||
|
||||
@@ -3412,10 +3412,10 @@
|
||||
<h2 id="833">8.3.3. 方法三:堆<a class="headerlink" href="#833" title="Permanent link">¶</a></h2>
|
||||
<p>我们可以基于堆更加高效地解决 Top-K 问题,流程如下:</p>
|
||||
<ol>
|
||||
<li>初始化一个小顶堆,其堆顶元素最小;</li>
|
||||
<li>先将数组的前 <span class="arithmatex">\(k\)</span> 个元素依次入堆;</li>
|
||||
<li>从第 <span class="arithmatex">\(k + 1\)</span> 个元素开始,若当前元素大于堆顶元素,则将堆顶元素出堆,并将当前元素入堆;</li>
|
||||
<li>遍历完成后,堆中保存的就是最大的 <span class="arithmatex">\(k\)</span> 个元素;</li>
|
||||
<li>初始化一个小顶堆,其堆顶元素最小。</li>
|
||||
<li>先将数组的前 <span class="arithmatex">\(k\)</span> 个元素依次入堆。</li>
|
||||
<li>从第 <span class="arithmatex">\(k + 1\)</span> 个元素开始,若当前元素大于堆顶元素,则将堆顶元素出堆,并将当前元素入堆。</li>
|
||||
<li>遍历完成后,堆中保存的就是最大的 <span class="arithmatex">\(k\)</span> 个元素。</li>
|
||||
</ol>
|
||||
<div class="tabbed-set tabbed-alternate" data-tabs="1:9"><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" /><div class="tabbed-labels"><label for="__tabbed_1_1"><1></label><label for="__tabbed_1_2"><2></label><label for="__tabbed_1_3"><3></label><label for="__tabbed_1_4"><4></label><label for="__tabbed_1_5"><5></label><label for="__tabbed_1_6"><6></label><label for="__tabbed_1_7"><7></label><label for="__tabbed_1_8"><8></label><label for="__tabbed_1_9"><9></label></div>
|
||||
<div class="tabbed-content">
|
||||
|
||||
Reference in New Issue
Block a user