This commit is contained in:
krahets
2023-08-22 13:50:24 +08:00
parent 77b90cd19b
commit b70b7c9e75
67 changed files with 580 additions and 580 deletions
+2 -2
View File
@@ -3597,9 +3597,9 @@
<p>将上述两者相乘,可得到建堆过程的时间复杂度为 <span class="arithmatex">\(O(n \log n)\)</span><strong>然而,这个估算结果并不准确,因为我们没有考虑到二叉树底层节点数量远多于顶层节点的特性</strong></p>
<p>接下来我们来进行更为详细的计算。为了减小计算难度,我们假设树是一个“完美二叉树”,该假设不会影响计算结果的正确性。设二叉树(即堆)节点数量为 <span class="arithmatex">\(n\)</span> ,树高度为 <span class="arithmatex">\(h\)</span></p>
<p><img alt="完美二叉树的各层节点数量" src="../build_heap.assets/heapify_operations_count.png" /></p>
<p align="center">完美二叉树的各层节点数量 </p>
<p align="center"> 8-5 &nbsp; 完美二叉树的各层节点数量 </p>
<p>图所示,<strong>节点“从顶至底堆化”的最大迭代次数等于该节点到叶节点的距离,而该距离正是“节点高度”</strong>。因此,我们可以将各层的“节点数量 <span class="arithmatex">\(\times\)</span> 节点高度”求和,<strong>从而得到所有节点的堆化迭代次数的总和</strong></p>
<p>如图 8-5 所示,<strong>节点“从顶至底堆化”的最大迭代次数等于该节点到叶节点的距离,而该距离正是“节点高度”</strong>。因此,我们可以将各层的“节点数量 <span class="arithmatex">\(\times\)</span> 节点高度”求和,<strong>从而得到所有节点的堆化迭代次数的总和</strong></p>
<div class="arithmatex">\[
T(h) = 2^0h + 2^1(h-1) + 2^2(h-2) + \dots + 2^{(h-1)}\times1
\]</div>
+15 -15
View File
@@ -3494,13 +3494,13 @@
<h1 id="81">8.1 &nbsp;<a class="headerlink" href="#81" title="Permanent link">&para;</a></h1>
<p>「堆 heap」是一种满足特定条件的完全二叉树,主要可分为图所示的两种类型:</p>
<p>「堆 heap」是一种满足特定条件的完全二叉树,主要可分为图 8-1 所示的两种类型:</p>
<ul>
<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">小顶堆与大顶堆 </p>
<p align="center"> 8-1 &nbsp; 小顶堆与大顶堆 </p>
<p>堆作为完全二叉树的一个特例,具有以下特性:</p>
<ul>
@@ -3511,8 +3511,8 @@
<h2 id="811">8.1.1 &nbsp; 堆常用操作<a class="headerlink" href="#811" title="Permanent link">&para;</a></h2>
<p>需要指出的是,许多编程语言提供的是「优先队列 priority queue」,这是一种抽象数据结构,定义为具有优先级排序的队列。</p>
<p>实际上,<strong>堆通常用作实现优先队列,大顶堆相当于元素按从大到小顺序出队的优先队列</strong>。从使用角度来看,我们可以将“优先队列”和“堆”看作等价的数据结构。因此,本书对两者不做特别区分,统一使用“堆“来命名。</p>
<p>堆的常用操作见表,方法名需要根据编程语言来确定。</p>
<p align="center">堆的操作效率 </p>
<p>堆的常用操作见表 8-1 ,方法名需要根据编程语言来确定。</p>
<p align="center"> 8-1 &nbsp; 堆的操作效率 </p>
<div class="center-table">
<table>
@@ -3578,11 +3578,11 @@
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a>
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a><span class="cm">/* 堆顶元素出堆 */</span>
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a><span class="c1">// 出堆元素会形成一个从大到小的序列</span>
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a><span class="n">peek</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">heap</span><span class="p">.</span><span class="na">poll</span><span class="p">();</span><span class="w"> </span><span class="c1">// 5</span>
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="n">peek</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">heap</span><span class="p">.</span><span class="na">poll</span><span class="p">();</span><span class="w"> </span><span class="c1">// 4</span>
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="n">peek</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">heap</span><span class="p">.</span><span class="na">poll</span><span class="p">();</span><span class="w"> </span><span class="c1">// 3</span>
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="n">peek</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">heap</span><span class="p">.</span><span class="na">poll</span><span class="p">();</span><span class="w"> </span><span class="c1">// 2</span>
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a><span class="n">peek</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">heap</span><span class="p">.</span><span class="na">poll</span><span class="p">();</span><span class="w"> </span><span class="c1">// 1</span>
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a><span class="n">peek</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">maxHeap</span><span class="p">.</span><span class="na">poll</span><span class="p">();</span><span class="w"> </span><span class="c1">// 5</span>
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a><span class="n">peek</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">maxHeap</span><span class="p">.</span><span class="na">poll</span><span class="p">();</span><span class="w"> </span><span class="c1">// 4</span>
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a><span class="n">peek</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">maxHeap</span><span class="p">.</span><span class="na">poll</span><span class="p">();</span><span class="w"> </span><span class="c1">// 3</span>
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a><span class="n">peek</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">maxHeap</span><span class="p">.</span><span class="na">poll</span><span class="p">();</span><span class="w"> </span><span class="c1">// 2</span>
<a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a><span class="n">peek</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">maxHeap</span><span class="p">.</span><span class="na">poll</span><span class="p">();</span><span class="w"> </span><span class="c1">// 1</span>
<a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a>
<a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a><span class="cm">/* 获取堆大小 */</span>
<a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a><span class="kt">int</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">maxHeap</span><span class="p">.</span><span class="na">size</span><span class="p">();</span>
@@ -3816,9 +3816,9 @@
<h3 id="1">1. &nbsp; 堆的存储与表示<a class="headerlink" href="#1" title="Permanent link">&para;</a></h3>
<p>我们在二叉树章节中学习到,完全二叉树非常适合用数组来表示。由于堆正是一种完全二叉树,<strong>我们将采用数组来存储堆</strong></p>
<p>当使用数组表示二叉树时,元素代表节点值,索引代表节点在二叉树中的位置。<strong>节点指针通过索引映射公式来实现</strong></p>
<p>图所示,给定索引 <span class="arithmatex">\(i\)</span> ,其左子节点索引为 <span class="arithmatex">\(2i + 1\)</span> ,右子节点索引为 <span class="arithmatex">\(2i + 2\)</span> ,父节点索引为 <span class="arithmatex">\((i - 1) / 2\)</span>(向下取整)。当索引越界时,表示空节点或节点不存在。</p>
<p>如图 8-2 所示,给定索引 <span class="arithmatex">\(i\)</span> ,其左子节点索引为 <span class="arithmatex">\(2i + 1\)</span> ,右子节点索引为 <span class="arithmatex">\(2i + 2\)</span> ,父节点索引为 <span class="arithmatex">\((i - 1) / 2\)</span>(向下取整)。当索引越界时,表示空节点或节点不存在。</p>
<p><img alt="堆的表示与存储" src="../heap.assets/representation_of_heap.png" /></p>
<p align="center">堆的表示与存储 </p>
<p align="center"> 8-2 &nbsp; 堆的表示与存储 </p>
<p>我们可以将索引映射公式封装成函数,方便后续使用。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="2:12"><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" /><input id="__tabbed_2_12" 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">JS</label><label for="__tabbed_2_6">TS</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><label for="__tabbed_2_12">Rust</label></div>
@@ -4119,7 +4119,7 @@
</div>
<h3 id="3">3. &nbsp; 元素入堆<a class="headerlink" href="#3" title="Permanent link">&para;</a></h3>
<p>给定元素 <code>val</code> ,我们首先将其添加到堆底。添加之后,由于 val 可能大于堆中其他元素,堆的成立条件可能已被破坏。因此,<strong>需要修复从插入节点到根节点的路径上的各个节点</strong>,这个操作被称为「堆化 heapify」。</p>
<p>考虑从入堆节点开始,<strong>从底至顶执行堆化</strong>。如图所示,我们比较插入节点与其父节点的值,如果插入节点更大,则将它们交换。然后继续执行此操作,从底至顶修复堆中的各个节点,直至越过根节点或遇到无须交换的节点时结束。</p>
<p>考虑从入堆节点开始,<strong>从底至顶执行堆化</strong>。如图 8-3 所示,我们比较插入节点与其父节点的值,如果插入节点更大,则将它们交换。然后继续执行此操作,从底至顶修复堆中的各个节点,直至越过根节点或遇到无须交换的节点时结束。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="4:9"><input checked="checked" id="__tabbed_4_1" name="__tabbed_4" type="radio" /><input id="__tabbed_4_2" name="__tabbed_4" type="radio" /><input id="__tabbed_4_3" name="__tabbed_4" type="radio" /><input id="__tabbed_4_4" name="__tabbed_4" type="radio" /><input id="__tabbed_4_5" name="__tabbed_4" type="radio" /><input id="__tabbed_4_6" name="__tabbed_4" type="radio" /><input id="__tabbed_4_7" name="__tabbed_4" type="radio" /><input id="__tabbed_4_8" name="__tabbed_4" type="radio" /><input id="__tabbed_4_9" name="__tabbed_4" type="radio" /><div class="tabbed-labels"><label for="__tabbed_4_1">&lt;1&gt;</label><label for="__tabbed_4_2">&lt;2&gt;</label><label for="__tabbed_4_3">&lt;3&gt;</label><label for="__tabbed_4_4">&lt;4&gt;</label><label for="__tabbed_4_5">&lt;5&gt;</label><label for="__tabbed_4_6">&lt;6&gt;</label><label for="__tabbed_4_7">&lt;7&gt;</label><label for="__tabbed_4_8">&lt;8&gt;</label><label for="__tabbed_4_9">&lt;9&gt;</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -4151,7 +4151,7 @@
</div>
</div>
</div>
<p align="center">元素入堆步骤 </p>
<p align="center"> 8-3 &nbsp; 元素入堆步骤 </p>
<p>设节点总数为 <span class="arithmatex">\(n\)</span> ,则树的高度为 <span class="arithmatex">\(O(\log n)\)</span> 。由此可知,堆化操作的循环轮数最多为 <span class="arithmatex">\(O(\log n)\)</span> <strong>元素入堆操作的时间复杂度为 <span class="arithmatex">\(O(\log n)\)</span></strong></p>
<div class="tabbed-set tabbed-alternate" data-tabs="5:12"><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" /><input id="__tabbed_5_12" 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">JS</label><label for="__tabbed_5_6">TS</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><label for="__tabbed_5_12">Rust</label></div>
@@ -4477,7 +4477,7 @@
<li>交换完成后,将堆底从列表中删除(注意,由于已经交换,实际上删除的是原来的堆顶元素)。</li>
<li>从根节点开始,<strong>从顶至底执行堆化</strong></li>
</ol>
<p>图所示,<strong>“从顶至底堆化”的操作方向与“从底至顶堆化”相反</strong>,我们将根节点的值与其两个子节点的值进行比较,将最大的子节点与根节点交换。然后循环执行此操作,直到越过叶节点或遇到无须交换的节点时结束。</p>
<p>如图 8-4 所示,<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">&lt;1&gt;</label><label for="__tabbed_6_2">&lt;2&gt;</label><label for="__tabbed_6_3">&lt;3&gt;</label><label for="__tabbed_6_4">&lt;4&gt;</label><label for="__tabbed_6_5">&lt;5&gt;</label><label for="__tabbed_6_6">&lt;6&gt;</label><label for="__tabbed_6_7">&lt;7&gt;</label><label for="__tabbed_6_8">&lt;8&gt;</label><label for="__tabbed_6_9">&lt;9&gt;</label><label for="__tabbed_6_10">&lt;10&gt;</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -4512,7 +4512,7 @@
</div>
</div>
</div>
<p align="center">堆顶元素出堆步骤 </p>
<p align="center"> 8-4 &nbsp; 堆顶元素出堆步骤 </p>
<p>与元素入堆操作相似,堆顶元素出堆操作的时间复杂度也为 <span class="arithmatex">\(O(\log n)\)</span></p>
<div class="tabbed-set tabbed-alternate" data-tabs="7:12"><input checked="checked" id="__tabbed_7_1" name="__tabbed_7" type="radio" /><input id="__tabbed_7_2" name="__tabbed_7" type="radio" /><input id="__tabbed_7_3" name="__tabbed_7" type="radio" /><input id="__tabbed_7_4" name="__tabbed_7" type="radio" /><input id="__tabbed_7_5" name="__tabbed_7" type="radio" /><input id="__tabbed_7_6" name="__tabbed_7" type="radio" /><input id="__tabbed_7_7" name="__tabbed_7" type="radio" /><input id="__tabbed_7_8" name="__tabbed_7" type="radio" /><input id="__tabbed_7_9" name="__tabbed_7" type="radio" /><input id="__tabbed_7_10" name="__tabbed_7" type="radio" /><input id="__tabbed_7_11" name="__tabbed_7" type="radio" /><input id="__tabbed_7_12" name="__tabbed_7" type="radio" /><div class="tabbed-labels"><label for="__tabbed_7_1">Java</label><label for="__tabbed_7_2">C++</label><label for="__tabbed_7_3">Python</label><label for="__tabbed_7_4">Go</label><label for="__tabbed_7_5">JS</label><label for="__tabbed_7_6">TS</label><label for="__tabbed_7_7">C</label><label for="__tabbed_7_8">C#</label><label for="__tabbed_7_9">Swift</label><label for="__tabbed_7_10">Zig</label><label for="__tabbed_7_11">Dart</label><label for="__tabbed_7_12">Rust</label></div>
+6 -6
View File
@@ -3432,23 +3432,23 @@
</div>
<p>对于该问题,我们先介绍两种思路比较直接的解法,再介绍效率更高的堆解法。</p>
<h2 id="831">8.3.1 &nbsp; 方法一:遍历选择<a class="headerlink" href="#831" title="Permanent link">&para;</a></h2>
<p>我们可以进行图所示的 <span class="arithmatex">\(k\)</span> 轮遍历,分别在每轮中提取第 <span class="arithmatex">\(1\)</span> , <span class="arithmatex">\(2\)</span> , <span class="arithmatex">\(\dots\)</span> , <span class="arithmatex">\(k\)</span> 大的元素,时间复杂度为 <span class="arithmatex">\(O(nk)\)</span></p>
<p>我们可以进行图 8-6 所示的 <span class="arithmatex">\(k\)</span> 轮遍历,分别在每轮中提取第 <span class="arithmatex">\(1\)</span> , <span class="arithmatex">\(2\)</span> , <span class="arithmatex">\(\dots\)</span> , <span class="arithmatex">\(k\)</span> 大的元素,时间复杂度为 <span class="arithmatex">\(O(nk)\)</span></p>
<p>此方法只适用于 <span class="arithmatex">\(k \ll n\)</span> 的情况,因为当 <span class="arithmatex">\(k\)</span><span class="arithmatex">\(n\)</span> 比较接近时,其时间复杂度趋向于 <span class="arithmatex">\(O(n^2)\)</span> ,非常耗时。</p>
<p><img alt="遍历寻找最大的 k 个元素" src="../top_k.assets/top_k_traversal.png" /></p>
<p align="center">遍历寻找最大的 k 个元素 </p>
<p align="center"> 8-6 &nbsp; 遍历寻找最大的 k 个元素 </p>
<div class="admonition tip">
<p class="admonition-title">Tip</p>
<p><span class="arithmatex">\(k = n\)</span> 时,我们可以得到从大到小的序列,等价于“选择排序”算法。</p>
</div>
<h2 id="832">8.3.2 &nbsp; 方法二:排序<a class="headerlink" href="#832" title="Permanent link">&para;</a></h2>
<p>图所示,我们可以先对数组 <code>nums</code> 进行排序,再返回最右边的 <span class="arithmatex">\(k\)</span> 个元素,时间复杂度为 <span class="arithmatex">\(O(n \log n)\)</span></p>
<p>如图 8-7 所示,我们可以先对数组 <code>nums</code> 进行排序,再返回最右边的 <span class="arithmatex">\(k\)</span> 个元素,时间复杂度为 <span class="arithmatex">\(O(n \log n)\)</span></p>
<p>显然,该方法“超额”完成任务了,因为我们只需要找出最大的 <span class="arithmatex">\(k\)</span> 个元素即可,而不需要排序其他元素。</p>
<p><img alt="排序寻找最大的 k 个元素" src="../top_k.assets/top_k_sorting.png" /></p>
<p align="center">排序寻找最大的 k 个元素 </p>
<p align="center"> 8-7 &nbsp; 排序寻找最大的 k 个元素 </p>
<h2 id="833">8.3.3 &nbsp; 方法三:堆<a class="headerlink" href="#833" title="Permanent link">&para;</a></h2>
<p>我们可以基于堆更加高效地解决 Top-K 问题,流程如图所示。</p>
<p>我们可以基于堆更加高效地解决 Top-K 问题,流程如图 8-8 所示。</p>
<ol>
<li>初始化一个小顶堆,其堆顶元素最小。</li>
<li>先将数组的前 <span class="arithmatex">\(k\)</span> 个元素依次入堆。</li>
@@ -3486,7 +3486,7 @@
</div>
</div>
</div>
<p align="center">基于堆寻找最大的 k 个元素 </p>
<p align="center"> 8-8 &nbsp; 基于堆寻找最大的 k 个元素 </p>
<p>总共执行了 <span class="arithmatex">\(n\)</span> 轮入堆和出堆,堆的最大长度为 <span class="arithmatex">\(k\)</span> ,因此时间复杂度为 <span class="arithmatex">\(O(n \log k)\)</span> 。该方法的效率很高,当 <span class="arithmatex">\(k\)</span> 较小时,时间复杂度趋向 <span class="arithmatex">\(O(n)\)</span> ;当 <span class="arithmatex">\(k\)</span> 较大时,时间复杂度不会超过 <span class="arithmatex">\(O(n \log n)\)</span></p>
<p>另外,该方法适用于动态数据流的使用场景。在不断加入数据时,我们可以持续维护堆内的元素,从而实现最大 <span class="arithmatex">\(k\)</span> 个元素的动态更新。</p>