Polish the chapter of stack_and_queue, tree

This commit is contained in:
krahets
2023-04-10 23:59:22 +08:00
parent 1bbfa85e08
commit 236b9cadb1
10 changed files with 162 additions and 180 deletions

View File

@@ -1,14 +1,14 @@
# 队列
「队列 Queue」是一种遵循先入先出first in, first out数据操作规则的线性数据结构。顾名思义,队列模拟的是排队现象,即外面的人不断加入队列尾部,而于队列头部的人不断地离开。
「队列 Queue」是一种遵循先入先出First In, First Out规则的线性数据结构。顾名思义队列模拟排队现象,即新来的人不断加入队列尾部,而于队列头部的人逐个离开。
我们队列头部称为「队首」,队列尾部称为「队尾」,把元素加入队尾的操作称为「入队」,删除队首元素的操作称为「出队」。
我们队列头部称为「队首」,尾部称为「队尾」,把元素加入队尾的操作称为「入队」,删除队首元素的操作称为「出队」。
![队列的先入先出规则](queue.assets/queue_operations.png)
## 队列常用操作
队列的常操作下表。需要注意,不同编程语言的方法名是不同的,在这里我们采用与栈相同的方法命名。
队列的常操作下表所示。需要注意的是,不同编程语言的方法名称可能会有所不同。我们在此采用与栈相同的方法命名。
<div class="center-table" markdown>
@@ -20,7 +20,7 @@
</div>
我们可以直接使用编程语言实现好的队列类。
我们可以直接使用编程语言中现成的队列类。
=== "Java"
@@ -254,11 +254,11 @@
## 队列实现
队列需要一种可以在一端添加,并在另一端删除的数据结构,也可以使用链表数组来实现。
为了实现队列,我们需要一种数据结构,可以在一端添加元素,并在另一端删除元素。因此,链表数组都可以用来实现队列
### 基于链表的实现
我们将链表的「头节点」和「尾节点」分别看作是队首和队尾,规定队尾可添加节点,队首可删除节点。
对于链表实现,我们可以将链表的「头节点」和「尾节点」分别视为队首和队尾,规定队尾可添加节点,队首可删除节点。
=== "LinkedListQueue"
![基于链表实现队列的入队出队操作](queue.assets/linkedlist_queue.png)
@@ -269,7 +269,7 @@
=== "pop()"
![linkedlist_queue_pop](queue.assets/linkedlist_queue_pop.png)
以下是使用链表实现队列的示例代码。
以下是用链表实现队列的示例代码。
=== "Java"
@@ -333,16 +333,16 @@
### 基于数组的实现
数组删除首元素的时间复杂度为 $O(n)$ ,这会导致出队操作效率低。然而,我们可以采取下述的巧妙方法来避免这个问题。
由于数组删除首元素的时间复杂度为 $O(n)$ ,这会导致出队操作效率低。然而,我们可以采用以下巧妙方法来避免这个问题。
考虑借助一个变量 `front` 指向队首元素的索引,并维护变量 `queSize` 记录队列长度。我们定义 `rear = front + queSize` 公式计算出的 `rear` 指向队尾元素索引 $+1$ ”的位置。
我们可以使用一个变量 `front` 指向队首元素的索引,并维护一个变量 `queSize` 用于记录队列长度。定义 `rear = front + queSize` 这个公式计算出的 `rear` 指向队尾元素之后的下一个位置。
在该设计**数组中包含元素的有效区间为 `[front, rear - 1]`** ,进而
基于此设计,**数组中包含元素的有效区间为 [front, rear - 1]**,进而
- 对于入队操作,将输入元素赋值给 `rear` 索引处,并将 `queSize` 自增 $1$ 即可
- 对于出队操作,需将 `front` 自增 $1$ ,并将 `queSize` 自减 $1$ 即可
- 对于入队操作,将输入元素赋值给 `rear` 索引处,并将 `queSize` 增加 1
- 对于出队操作,需将 `front` 增加 1 ,并将 `queSize` 减少 1
观察发现,入队出队操作都仅需单次操作即可完成,时间复杂度为 $O(1)$ 。
可以看到,入队出队操作都只需进行一次操作,时间复杂度为 $O(1)$ 。
=== "ArrayQueue"
![基于数组实现队列的入队出队操作](queue.assets/array_queue.png)
@@ -353,9 +353,9 @@
=== "pop()"
![array_queue_pop](queue.assets/array_queue_pop.png)
细心的同学可能会发现一个问题:在不断入队出队的过程中,`front` 和 `rear` 都在向右移动,**到达数组尾部就无法继续移动了**。为解决此问题,**我们考虑将数组看作是首尾相接的**,这样的数组被称为「环形数组」。
可能会发现一个问题:在不断进行入队出队的过程中,`front` 和 `rear` 都在向右移动,**当它们到达数组尾部就无法继续移动了**。为解决此问题,我们可以将数组视为首尾相接的「环形数组」。
对于环形数组,我们需要 `front` 或 `rear` 在越过数组尾部,直接回到数组头部续遍历。这种周期性规律可以通过取余操作来实现,详情请见以下代码。
对于环形数组,我们需要 `front` 或 `rear` 在越过数组尾部,直接回到数组头部续遍历。这种周期性规律可以通过取余操作来实现,代码如下所示
=== "Java"
@@ -417,13 +417,11 @@
[class]{ArrayQueue}-[func]{}
```
以上实现的队列仍存在局限性,即长度不可变。不过这个问题很容易解决,我们可以将数组替换为列表(即动态数组,从而引入扩容机制。有兴趣的同学可以尝试自行实现。
以上实现的队列仍然具有局限性,即长度不可变。然而,这个问题不难解决,我们可以将数组替换为动态数组,从而引入扩容机制。有兴趣的同学可以尝试自行实现。
## 两种实现对比
与栈的结论一致,在此不再赘述。
两种实现对比结论与栈一致,在此不再赘述。
## 队列典型应用
- **淘宝订单**。购物者下单后,订单就被加入队列中,随后系统再根据顺序依次处理队列中的订单。在双十一时,在短时间内会产生海量订单,如何处理「高并发」则是工程师们需要重点思考的问题。
- **各待办事项**。任何需要实现“先来后到”功能,例如打印机的任务队列、餐厅的出餐队列等
- **淘宝订单**。购物者下单后,订单加入队列中,系统随后会根据顺序依次处理队列中的订单。在双十一期间,短时间内会产生海量订单,高并发成为工程师们需要重点攻克的问题。
- **各待办事项**。任何需要实现“先来后到”功能的场景,例如打印机的任务队列、餐厅的出餐队列等。队列在这些场景中可以有效地维护处理顺序