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
+8 -8
View File
@@ -3466,13 +3466,13 @@
<h1 id="53">5.3 &nbsp; 双向队列<a class="headerlink" href="#53" title="Permanent link">&para;</a></h1>
<p>在队列中,我们仅能在头部删除或在尾部添加元素。如图所示,「双向队列 deque」提供了更高的灵活性,允许在头部和尾部执行元素的添加或删除操作。</p>
<p>在队列中,我们仅能在头部删除或在尾部添加元素。如图 5-7 所示,「双向队列 deque」提供了更高的灵活性,允许在头部和尾部执行元素的添加或删除操作。</p>
<p><img alt="双向队列的操作" src="../deque.assets/deque_operations.png" /></p>
<p align="center">双向队列的操作 </p>
<p align="center"> 5-7 &nbsp; 双向队列的操作 </p>
<h2 id="531">5.3.1 &nbsp; 双向队列常用操作<a class="headerlink" href="#531" title="Permanent link">&para;</a></h2>
<p>双向队列的常用操作如表所示,具体的方法名称需要根据所使用的编程语言来确定。</p>
<p align="center">双向队列操作效率 </p>
<p>双向队列的常用操作如表 5-3 所示,具体的方法名称需要根据所使用的编程语言来确定。</p>
<p align="center"> 5-3 &nbsp; 双向队列操作效率 </p>
<div class="center-table">
<table>
@@ -3798,7 +3798,7 @@
<h3 id="1">1. &nbsp; 基于双向链表的实现<a class="headerlink" href="#1" title="Permanent link">&para;</a></h3>
<p>回顾上一节内容,我们使用普通单向链表来实现队列,因为它可以方便地删除头节点(对应出队操作)和在尾节点后添加新节点(对应入队操作)。</p>
<p>对于双向队列而言,头部和尾部都可以执行入队和出队操作。换句话说,双向队列需要实现另一个对称方向的操作。为此,我们采用“双向链表”作为双向队列的底层数据结构。</p>
<p>图所示,我们将双向链表的头节点和尾节点视为双向队列的队首和队尾,同时实现在两端添加和删除节点的功能。</p>
<p>如图 5-8 所示,我们将双向链表的头节点和尾节点视为双向队列的队首和队尾,同时实现在两端添加和删除节点的功能。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="2:5"><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" /><div class="tabbed-labels"><label for="__tabbed_2_1">LinkedListDeque</label><label for="__tabbed_2_2">pushLast()</label><label for="__tabbed_2_3">pushFirst()</label><label for="__tabbed_2_4">popLast()</label><label for="__tabbed_2_5">popFirst()</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -3818,7 +3818,7 @@
</div>
</div>
</div>
<p align="center">基于链表实现双向队列的入队出队操作 </p>
<p align="center"> 5-8 &nbsp; 基于链表实现双向队列的入队出队操作 </p>
<p>实现代码如下所示。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="3:12"><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" /><input id="__tabbed_3_12" name="__tabbed_3" type="radio" /><div class="tabbed-labels"><label for="__tabbed_3_1">Java</label><label for="__tabbed_3_2">C++</label><label for="__tabbed_3_3">Python</label><label for="__tabbed_3_4">Go</label><label for="__tabbed_3_5">JS</label><label for="__tabbed_3_6">TS</label><label for="__tabbed_3_7">C</label><label for="__tabbed_3_8">C#</label><label for="__tabbed_3_9">Swift</label><label for="__tabbed_3_10">Zig</label><label for="__tabbed_3_11">Dart</label><label for="__tabbed_3_12">Rust</label></div>
@@ -5418,7 +5418,7 @@
</div>
</div>
<h3 id="2">2. &nbsp; 基于数组的实现<a class="headerlink" href="#2" title="Permanent link">&para;</a></h3>
<p>图所示,与基于数组实现队列类似,我们也可以使用环形数组来实现双向队列。</p>
<p>如图 5-9 所示,与基于数组实现队列类似,我们也可以使用环形数组来实现双向队列。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="4:5"><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" /><div class="tabbed-labels"><label for="__tabbed_4_1">ArrayDeque</label><label for="__tabbed_4_2">pushLast()</label><label for="__tabbed_4_3">pushFirst()</label><label for="__tabbed_4_4">popLast()</label><label for="__tabbed_4_5">popFirst()</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -5438,7 +5438,7 @@
</div>
</div>
</div>
<p align="center">基于数组实现双向队列的入队出队操作 </p>
<p align="center"> 5-9 &nbsp; 基于数组实现双向队列的入队出队操作 </p>
<p>在队列的实现基础上,仅需增加“队首入队”和“队尾出队”的方法。</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>
+8 -8
View File
@@ -3467,13 +3467,13 @@
<h1 id="52">5.2 &nbsp; 队列<a class="headerlink" href="#52" title="Permanent link">&para;</a></h1>
<p>「队列 queue」是一种遵循先入先出规则的线性数据结构。顾名思义,队列模拟了排队现象,即新来的人不断加入队列的尾部,而位于队列头部的人逐个离开。</p>
<p>图所示,我们将队列的头部称为“队首”,尾部称为“队尾”,将把元素加入队尾的操作称为“入队”,删除队首元素的操作称为“出队”。</p>
<p>如图 5-4 所示,我们将队列的头部称为“队首”,尾部称为“队尾”,将把元素加入队尾的操作称为“入队”,删除队首元素的操作称为“出队”。</p>
<p><img alt="队列的先入先出规则" src="../queue.assets/queue_operations.png" /></p>
<p align="center">队列的先入先出规则 </p>
<p align="center"> 5-4 &nbsp; 队列的先入先出规则 </p>
<h2 id="521">5.2.1 &nbsp; 队列常用操作<a class="headerlink" href="#521" title="Permanent link">&para;</a></h2>
<p>队列的常见操作如表所示。需要注意的是,不同编程语言的方法名称可能会有所不同。我们在此采用与栈相同的方法命名。</p>
<p align="center">队列操作效率 </p>
<p>队列的常见操作如表 5-2 所示。需要注意的是,不同编程语言的方法名称可能会有所不同。我们在此采用与栈相同的方法命名。</p>
<p align="center"> 5-2 &nbsp; 队列操作效率 </p>
<div class="center-table">
<table>
@@ -3750,7 +3750,7 @@
<h2 id="522">5.2.2 &nbsp; 队列实现<a class="headerlink" href="#522" title="Permanent link">&para;</a></h2>
<p>为了实现队列,我们需要一种数据结构,可以在一端添加元素,并在另一端删除元素。因此,链表和数组都可以用来实现队列。</p>
<h3 id="1">1. &nbsp; 基于链表的实现<a class="headerlink" href="#1" title="Permanent link">&para;</a></h3>
<p>图所示,我们可以将链表的“头节点”和“尾节点”分别视为“队首”和“队尾”,规定队尾仅可添加节点,队首仅可删除节点。</p>
<p>如图 5-5 所示,我们可以将链表的“头节点”和“尾节点”分别视为“队首”和“队尾”,规定队尾仅可添加节点,队首仅可删除节点。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="2:3"><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" /><div class="tabbed-labels"><label for="__tabbed_2_1">LinkedListQueue</label><label for="__tabbed_2_2">push()</label><label for="__tabbed_2_3">pop()</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -3764,7 +3764,7 @@
</div>
</div>
</div>
<p align="center">基于链表实现队列的入队出队操作 </p>
<p align="center"> 5-5 &nbsp; 基于链表实现队列的入队出队操作 </p>
<p>以下是用链表实现队列的代码。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="3:12"><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" /><input id="__tabbed_3_12" name="__tabbed_3" type="radio" /><div class="tabbed-labels"><label for="__tabbed_3_1">Java</label><label for="__tabbed_3_2">C++</label><label for="__tabbed_3_3">Python</label><label for="__tabbed_3_4">Go</label><label for="__tabbed_3_5">JS</label><label for="__tabbed_3_6">TS</label><label for="__tabbed_3_7">C</label><label for="__tabbed_3_8">C#</label><label for="__tabbed_3_9">Swift</label><label for="__tabbed_3_10">Zig</label><label for="__tabbed_3_11">Dart</label><label for="__tabbed_3_12">Rust</label></div>
@@ -4618,7 +4618,7 @@
<h3 id="2">2. &nbsp; 基于数组的实现<a class="headerlink" href="#2" title="Permanent link">&para;</a></h3>
<p>由于数组删除首元素的时间复杂度为 <span class="arithmatex">\(O(n)\)</span> ,这会导致出队操作效率较低。然而,我们可以采用以下巧妙方法来避免这个问题。</p>
<p>我们可以使用一个变量 <code>front</code> 指向队首元素的索引,并维护一个变量 <code>queSize</code> 用于记录队列长度。定义 <code>rear = front + queSize</code> ,这个公式计算出的 <code>rear</code> 指向队尾元素之后的下一个位置。</p>
<p>基于此设计,<strong>数组中包含元素的有效区间为 <code>[front, rear - 1]</code></strong>,各种操作的实现方法如图所示。</p>
<p>基于此设计,<strong>数组中包含元素的有效区间为 <code>[front, rear - 1]</code></strong>,各种操作的实现方法如图 5-6 所示。</p>
<ul>
<li>入队操作:将输入元素赋值给 <code>rear</code> 索引处,并将 <code>queSize</code> 增加 1 。</li>
<li>出队操作:只需将 <code>front</code> 增加 1 ,并将 <code>queSize</code> 减少 1 。</li>
@@ -4637,7 +4637,7 @@
</div>
</div>
</div>
<p align="center">基于数组实现队列的入队出队操作 </p>
<p align="center"> 5-6 &nbsp; 基于数组实现队列的入队出队操作 </p>
<p>你可能会发现一个问题:在不断进行入队和出队的过程中,<code>front</code><code>rear</code> 都在向右移动,<strong>当它们到达数组尾部时就无法继续移动了</strong>。为解决此问题,我们可以将数组视为首尾相接的“环形数组”。</p>
<p>对于环形数组,我们需要让 <code>front</code><code>rear</code> 在越过数组尾部时,直接回到数组头部继续遍历。这种周期性规律可以通过“取余操作”来实现,代码如下所示。</p>
+8 -8
View File
@@ -3482,13 +3482,13 @@
<h1 id="51">5.1 &nbsp;<a class="headerlink" href="#51" title="Permanent link">&para;</a></h1>
<p>「栈 stack」是一种遵循先入后出的逻辑的线性数据结构。</p>
<p>我们可以将栈类比为桌面上的一摞盘子,如果需要拿出底部的盘子,则需要先将上面的盘子依次取出。我们将盘子替换为各种类型的元素(如整数、字符、对象等),就得到了栈数据结构。</p>
<p>图所示,我们把堆叠元素的顶部称为“栈顶”,底部称为“栈底”。将把元素添加到栈顶的操作叫做“入栈”,删除栈顶元素的操作叫做“出栈”。</p>
<p>如图 5-1 所示,我们把堆叠元素的顶部称为“栈顶”,底部称为“栈底”。将把元素添加到栈顶的操作叫做“入栈”,删除栈顶元素的操作叫做“出栈”。</p>
<p><img alt="栈的先入后出规则" src="../stack.assets/stack_operations.png" /></p>
<p align="center">栈的先入后出规则 </p>
<p align="center"> 5-1 &nbsp; 栈的先入后出规则 </p>
<h2 id="511">5.1.1 &nbsp; 栈常用操作<a class="headerlink" href="#511" title="Permanent link">&para;</a></h2>
<p>栈的常用操作如表所示,具体的方法名需要根据所使用的编程语言来确定。在此,我们以常见的 <code>push()</code> , <code>pop()</code> , <code>peek()</code> 命名为例。</p>
<p align="center">栈的操作效率 </p>
<p>栈的常用操作如表 5-1 所示,具体的方法名需要根据所使用的编程语言来确定。在此,我们以常见的 <code>push()</code> , <code>pop()</code> , <code>peek()</code> 命名为例。</p>
<p align="center"> 5-1 &nbsp; 栈的操作效率 </p>
<div class="center-table">
<table>
@@ -3763,7 +3763,7 @@
<p>栈遵循先入后出的原则,因此我们只能在栈顶添加或删除元素。然而,数组和链表都可以在任意位置添加和删除元素,<strong>因此栈可以被视为一种受限制的数组或链表</strong>。换句话说,我们可以“屏蔽”数组或链表的部分无关操作,使其对外表现的逻辑符合栈的特性。</p>
<h3 id="1">1. &nbsp; 基于链表的实现<a class="headerlink" href="#1" title="Permanent link">&para;</a></h3>
<p>使用链表来实现栈时,我们可以将链表的头节点视为栈顶,尾节点视为栈底。</p>
<p>图所示,对于入栈操作,我们只需将元素插入链表头部,这种节点插入方法被称为“头插法”。而对于出栈操作,只需将头节点从链表中删除即可。</p>
<p>如图 5-2 所示,对于入栈操作,我们只需将元素插入链表头部,这种节点插入方法被称为“头插法”。而对于出栈操作,只需将头节点从链表中删除即可。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="2:3"><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" /><div class="tabbed-labels"><label for="__tabbed_2_1">LinkedListStack</label><label for="__tabbed_2_2">push()</label><label for="__tabbed_2_3">pop()</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -3777,7 +3777,7 @@
</div>
</div>
</div>
<p align="center">基于链表实现栈的入栈出栈操作 </p>
<p align="center"> 5-2 &nbsp; 基于链表实现栈的入栈出栈操作 </p>
<p>以下是基于链表实现栈的示例代码。</p>
<div class="tabbed-set tabbed-alternate" data-tabs="3:12"><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" /><input id="__tabbed_3_12" name="__tabbed_3" type="radio" /><div class="tabbed-labels"><label for="__tabbed_3_1">Java</label><label for="__tabbed_3_2">C++</label><label for="__tabbed_3_3">Python</label><label for="__tabbed_3_4">Go</label><label for="__tabbed_3_5">JS</label><label for="__tabbed_3_6">TS</label><label for="__tabbed_3_7">C</label><label for="__tabbed_3_8">C#</label><label for="__tabbed_3_9">Swift</label><label for="__tabbed_3_10">Zig</label><label for="__tabbed_3_11">Dart</label><label for="__tabbed_3_12">Rust</label></div>
@@ -4513,7 +4513,7 @@
</div>
</div>
<h3 id="2">2. &nbsp; 基于数组的实现<a class="headerlink" href="#2" title="Permanent link">&para;</a></h3>
<p>使用数组实现栈时,我们可以将数组的尾部作为栈顶。如图所示,入栈与出栈操作分别对应在数组尾部添加元素与删除元素,时间复杂度都为 <span class="arithmatex">\(O(1)\)</span></p>
<p>使用数组实现栈时,我们可以将数组的尾部作为栈顶。如图 5-3 所示,入栈与出栈操作分别对应在数组尾部添加元素与删除元素,时间复杂度都为 <span class="arithmatex">\(O(1)\)</span></p>
<div class="tabbed-set tabbed-alternate" data-tabs="4:3"><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" /><div class="tabbed-labels"><label for="__tabbed_4_1">ArrayStack</label><label for="__tabbed_4_2">push()</label><label for="__tabbed_4_3">pop()</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
@@ -4527,7 +4527,7 @@
</div>
</div>
</div>
<p align="center">基于数组实现栈的入栈出栈操作 </p>
<p align="center"> 5-3 &nbsp; 基于数组实现栈的入栈出栈操作 </p>
<p>由于入栈的元素可能会源源不断地增加,因此我们可以使用动态数组,这样就无须自行处理数组扩容问题。以下为示例代码。</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>