You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
hello-algo/chapter_stack_and_queue/queue.md

1635 lines
46 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
comments: true
---
# 5.2.   队列
「队列 Queue」是一种遵循「先入先出 first in, first out」数据操作规则的线性数据结构。顾名思义队列模拟的是排队现象即外面的人不断加入队列尾部而处于队列头部的人不断地离开。
我们将队列头部称为「队首」,队列尾部称为「队尾」,将把元素加入队尾的操作称为「入队」,删除队首元素的操作称为「出队」。
![队列的先入先出规则](queue.assets/queue_operations.png)
<p align="center"> Fig. 队列的先入先出规则 </p>
## 5.2.1. &nbsp; 队列常用操作
队列的常用操作见下表。需要注意,不同编程语言的方法名是不同的,在这里我们采用与栈相同的方法命名。
<div class="center-table" markdown>
| 方法名 | 描述 | 时间复杂度 |
| --------- | -------------------------- | -------- |
| push() | 元素入队,即将元素添加至队尾 | $O(1)$ |
| pop() | 队首元素出队 | $O(1)$ |
| peek() | 访问队首元素 | $O(1)$ |
</div>
我们可以直接使用编程语言实现好的队列类。
=== "Java"
```java title="queue.java"
/* 初始化队列 */
Queue<Integer> queue = new LinkedList<>();
/* 元素入队 */
queue.offer(1);
queue.offer(3);
queue.offer(2);
queue.offer(5);
queue.offer(4);
/* 访问队首元素 */
int peek = queue.peek();
/* 元素出队 */
int pop = queue.poll();
/* 获取队列的长度 */
int size = queue.size();
/* 判断队列是否为空 */
boolean isEmpty = queue.isEmpty();
```
=== "C++"
```cpp title="queue.cpp"
/* 初始化队列 */
queue<int> queue;
/* 元素入队 */
queue.push(1);
queue.push(3);
queue.push(2);
queue.push(5);
queue.push(4);
/* 访问队首元素 */
int front = queue.front();
/* 元素出队 */
queue.pop();
/* 获取队列的长度 */
int size = queue.size();
/* 判断队列是否为空 */
bool empty = queue.empty();
```
=== "Python"
```python title="queue.py"
""" 初始化队列 """
# 在 Python 中,我们一般将双向队列类 deque 看作队列使用
# 虽然 queue.Queue() 是纯正的队列类,但不太好用,因此不建议
que: Deque[int] = collections.deque()
""" 元素入队 """
que.append(1)
que.append(3)
que.append(2)
que.append(5)
que.append(4)
""" 访问队首元素 """
front: int = que[0];
""" 元素出队 """
pop: int = que.popleft()
""" 获取队列的长度 """
size: int = len(que)
""" 判断队列是否为空 """
is_empty: bool = len(que) == 0
```
=== "Go"
```go title="queue_test.go"
/* 初始化队列 */
// 在 Go 中,将 list 作为队列来使用
queue := list.New()
/* 元素入队 */
queue.PushBack(1)
queue.PushBack(3)
queue.PushBack(2)
queue.PushBack(5)
queue.PushBack(4)
/* 访问队首元素 */
peek := queue.Front()
/* 元素出队 */
pop := queue.Front()
queue.Remove(pop)
/* 获取队列的长度 */
size := queue.Len()
/* 判断队列是否为空 */
isEmpty := queue.Len() == 0
```
=== "JavaScript"
```javascript title="queue.js"
/* 初始化队列 */
// JavaScript 没有内置的队列,可以把 Array 当作队列来使用
const queue = [];
/* 元素入队 */
queue.push(1);
queue.push(3);
queue.push(2);
queue.push(5);
queue.push(4);
/* 访问队首元素 */
const peek = queue[0];
/* 元素出队 */
// 底层是数组,因此 shift() 方法的时间复杂度为 O(n)
const pop = queue.shift();
/* 获取队列的长度 */
const size = queue.length;
/* 判断队列是否为空 */
const empty = queue.length === 0;
```
=== "TypeScript"
```typescript title="queue.ts"
/* 初始化队列 */
// TypeScript 没有内置的队列,可以把 Array 当作队列来使用
const queue: number[] = [];
/* 元素入队 */
queue.push(1);
queue.push(3);
queue.push(2);
queue.push(5);
queue.push(4);
/* 访问队首元素 */
const peek = queue[0];
/* 元素出队 */
// 底层是数组,因此 shift() 方法的时间复杂度为 O(n)
const pop = queue.shift();
/* 获取队列的长度 */
const size = queue.length;
/* 判断队列是否为空 */
const empty = queue.length === 0;
```
=== "C"
```c title="queue.c"
```
=== "C#"
```csharp title="queue.cs"
/* 初始化队列 */
Queue<int> queue = new();
/* 元素入队 */
queue.Enqueue(1);
queue.Enqueue(3);
queue.Enqueue(2);
queue.Enqueue(5);
queue.Enqueue(4);
/* 访问队首元素 */
int peek = queue.Peek();
/* 元素出队 */
int pop = queue.Dequeue();
/* 获取队列的长度 */
int size = queue.Count();
/* 判断队列是否为空 */
bool isEmpty = queue.Count() == 0;
```
=== "Swift"
```swift title="queue.swift"
/* 初始化队列 */
// Swift 没有内置的队列类,可以把 Array 当作队列来使用
var queue: [Int] = []
/* 元素入队 */
queue.append(1)
queue.append(3)
queue.append(2)
queue.append(5)
queue.append(4)
/* 访问队首元素 */
let peek = queue.first!
/* 元素出队 */
// 由于是数组,因此 removeFirst 的复杂度为 O(n)
let pool = queue.removeFirst()
/* 获取队列的长度 */
let size = queue.count
/* 判断队列是否为空 */
let isEmpty = queue.isEmpty
```
=== "Zig"
```zig title="queue.zig"
```
## 5.2.2. &nbsp; 队列实现
队列需要一种可以在一端添加,并在另一端删除的数据结构,也可以使用链表或数组来实现。
### 基于链表的实现
我们将链表的「头结点」和「尾结点」分别看作是队首和队尾,并规定队尾只可添加结点,队首只可删除结点。
=== "LinkedListQueue"
![基于链表实现队列的入队出队操作](queue.assets/linkedlist_queue.png)
=== "push()"
![linkedlist_queue_push](queue.assets/linkedlist_queue_push.png)
=== "pop()"
![linkedlist_queue_pop](queue.assets/linkedlist_queue_pop.png)
以下是使用链表实现队列的示例代码。
=== "Java"
```java title="linkedlist_queue.java"
/* 基于链表实现的队列 */
class LinkedListQueue {
private ListNode front, rear; // 头结点 front ,尾结点 rear
private int queSize = 0;
public LinkedListQueue() {
front = null;
rear = null;
}
/* 获取队列的长度 */
public int size() {
return queSize;
}
/* 判断队列是否为空 */
public boolean isEmpty() {
return size() == 0;
}
/* 入队 */
public void push(int num) {
// 尾结点后添加 num
ListNode node = new ListNode(num);
// 如果队列为空,则令头、尾结点都指向该结点
if (front == null) {
front = node;
rear = node;
// 如果队列不为空,则将该结点添加到尾结点后
} else {
rear.next = node;
rear = node;
}
queSize++;
}
/* 出队 */
public int pop() {
int num = peek();
// 删除头结点
front = front.next;
queSize--;
return num;
}
/* 访问队首元素 */
public int peek() {
if (size() == 0)
throw new EmptyStackException();
return front.val;
}
/* 将链表转化为 Array 并返回 */
public int[] toArray() {
ListNode node = front;
int[] res = new int[size()];
for (int i = 0; i < res.length; i++) {
res[i] = node.val;
node = node.next;
}
return res;
}
}
```
=== "C++"
```cpp title="linkedlist_queue.cpp"
/* 基于链表实现的队列 */
class LinkedListQueue {
private:
ListNode *front, *rear; // 头结点 front ,尾结点 rear
int queSize;
public:
LinkedListQueue() {
front = nullptr;
rear = nullptr;
queSize = 0;
}
~LinkedListQueue() {
delete front;
delete rear;
}
/* 获取队列的长度 */
int size() {
return queSize;
}
/* 判断队列是否为空 */
bool empty() {
return queSize == 0;
}
/* 入队 */
void push(int num) {
// 尾结点后添加 num
ListNode* node = new ListNode(num);
// 如果队列为空,则令头、尾结点都指向该结点
if (front == nullptr) {
front = node;
rear = node;
}
// 如果队列不为空,则将该结点添加到尾结点后
else {
rear->next = node;
rear = node;
}
queSize++;
}
/* 出队 */
void pop() {
int num = peek();
// 删除头结点
ListNode *tmp = front;
front = front->next;
// 释放内存
delete tmp;
queSize--;
}
/* 访问队首元素 */
int peek() {
if (size() == 0)
throw out_of_range("队列为空");
return front->val;
}
/* 将链表转化为 Vector 并返回 */
vector<int> toVector() {
ListNode* node = front;
vector<int> res(size());
for (int i = 0; i < res.size(); i++) {
res[i] = node->val;
node = node->next;
}
return res;
}
};
```
=== "Python"
```python title="linkedlist_queue.py"
class LinkedListQueue:
""" 基于链表实现的队列 """
def __init__(self):
""" 构造方法 """
self.__front: Optional[ListNode] = None # 头结点 front
self.__rear: Optional[ListNode] = None # 尾结点 rear
self.__size: int = 0
def size(self) -> int:
""" 获取队列的长度 """
return self.__size
def is_empty(self) -> bool:
""" 判断队列是否为空 """
return not self.__front
def push(self, num: int) -> None:
""" 入队 """
# 尾结点后添加 num
node = ListNode(num)
# 如果队列为空,则令头、尾结点都指向该结点
if self.__front is None:
self.__front = node
self.__rear = node
# 如果队列不为空,则将该结点添加到尾结点后
else:
self.__rear.next = node
self.__rear = node
self.__size += 1
def pop(self) -> int:
""" 出队 """
num = self.peek()
# 删除头结点
self.__front = self.__front.next
self.__size -= 1
return num
def peek(self) -> int:
""" 访问队首元素 """
if self.size() == 0:
print("队列为空")
return False
return self.__front.val
def to_list(self) -> List[int]:
""" 转化为列表用于打印 """
queue = []
temp = self.__front
while temp:
queue.append(temp.val)
temp = temp.next
return queue
```
=== "Go"
```go title="linkedlist_queue.go"
/* 基于链表实现的队列 */
type linkedListQueue struct {
// 使用内置包 list 来实现队列
data *list.List
}
/* 初始化队列 */
func newLinkedListQueue() *linkedListQueue {
return &linkedListQueue{
data: list.New(),
}
}
/* 入队 */
func (s *linkedListQueue) push(value any) {
s.data.PushBack(value)
}
/* 出队 */
func (s *linkedListQueue) pop() any {
if s.isEmpty() {
return nil
}
e := s.data.Front()
s.data.Remove(e)
return e.Value
}
/* 访问队首元素 */
func (s *linkedListQueue) peek() any {
if s.isEmpty() {
return nil
}
e := s.data.Front()
return e.Value
}
/* 获取队列的长度 */
func (s *linkedListQueue) size() int {
return s.data.Len()
}
/* 判断队列是否为空 */
func (s *linkedListQueue) isEmpty() bool {
return s.data.Len() == 0
}
/* 获取 List 用于打印 */
func (s *linkedListQueue) toList() *list.List {
return s.data
}
```
=== "JavaScript"
```javascript title="linkedlist_queue.js"
/* 基于链表实现的队列 */
class LinkedListQueue {
#front; // 头结点 #front
#rear; // 尾结点 #rear
#queSize = 0;
constructor() {
this.#front = null;
this.#rear = null;
}
/* 获取队列的长度 */
get size() {
return this.#queSize;
}
/* 判断队列是否为空 */
isEmpty() {
return this.size === 0;
}
/* 入队 */
push(num) {
// 尾结点后添加 num
const node = new ListNode(num);
// 如果队列为空,则令头、尾结点都指向该结点
if (!this.#front) {
this.#front = node;
this.#rear = node;
// 如果队列不为空,则将该结点添加到尾结点后
} else {
this.#rear.next = node;
this.#rear = node;
}
this.#queSize++;
}
/* 出队 */
pop() {
const num = this.peek();
// 删除头结点
this.#front = this.#front.next;
this.#queSize--;
return num;
}
/* 访问队首元素 */
peek() {
if (this.size === 0)
throw new Error("队列为空");
return this.#front.val;
}
/* 将链表转化为 Array 并返回 */
toArray() {
let node = this.#front;
const res = new Array(this.size);
for (let i = 0; i < res.length; i++) {
res[i] = node.val;
node = node.next;
}
return res;
}
}
```
=== "TypeScript"
```typescript title="linkedlist_queue.ts"
/* 基于链表实现的队列 */
class LinkedListQueue {
private front: ListNode | null; // 头结点 front
private rear: ListNode | null; // 尾结点 rear
private queSize: number = 0;
constructor() {
this.front = null;
this.rear = null;
}
/* 获取队列的长度 */
get size(): number {
return this.queSize;
}
/* 判断队列是否为空 */
isEmpty(): boolean {
return this.size === 0;
}
/* 入队 */
push(num: number): void {
// 尾结点后添加 num
const node = new ListNode(num);
// 如果队列为空,则令头、尾结点都指向该结点
if (!this.front) {
this.front = node;
this.rear = node;
// 如果队列不为空,则将该结点添加到尾结点后
} else {
this.rear!.next = node;
this.rear = node;
}
this.queSize++;
}
/* 出队 */
pop(): number {
const num = this.peek();
if (!this.front) throw new Error('队列为空');
// 删除头结点
this.front = this.front.next;
this.queSize--;
return num;
}
/* 访问队首元素 */
peek(): number {
if (this.size === 0) throw new Error('队列为空');
return this.front!.val;
}
/* 将链表转化为 Array 并返回 */
toArray(): number[] {
let node = this.front;
const res = new Array<number>(this.size);
for (let i = 0; i < res.length; i++) {
res[i] = node!.val;
node = node!.next;
}
return res;
}
}
```
=== "C"
```c title="linkedlist_queue.c"
[class]{linkedListQueue}-[func]{}
```
=== "C#"
```csharp title="linkedlist_queue.cs"
/* 基于链表实现的队列 */
class LinkedListQueue
{
private ListNode? front, rear; // 头结点 front ,尾结点 rear
private int queSize = 0;
public LinkedListQueue()
{
front = null;
rear = null;
}
/* 获取队列的长度 */
public int size()
{
return queSize;
}
/* 判断队列是否为空 */
public bool isEmpty()
{
return size() == 0;
}
/* 入队 */
public void push(int num)
{
// 尾结点后添加 num
ListNode node = new ListNode(num);
// 如果队列为空,则令头、尾结点都指向该结点
if (front == null)
{
front = node;
rear = node;
// 如果队列不为空,则将该结点添加到尾结点后
}
else if (rear != null)
{
rear.next = node;
rear = node;
}
queSize++;
}
/* 出队 */
public int pop()
{
int num = peek();
// 删除头结点
front = front?.next;
queSize--;
return num;
}
/* 访问队首元素 */
public int peek()
{
if (size() == 0 || front == null)
throw new Exception();
return front.val;
}
/* 将链表转化为 Array 并返回 */
public int[] toArray()
{
if (front == null)
return Array.Empty<int>();
ListNode node = front;
int[] res = new int[size()];
for (int i = 0; i < res.Length; i++)
{
res[i] = node.val;
node = node.next;
}
return res;
}
}
```
=== "Swift"
```swift title="linkedlist_queue.swift"
/* 基于链表实现的队列 */
class LinkedListQueue {
private var front: ListNode? // 头结点
private var rear: ListNode? // 尾结点
private var _size = 0
init() {}
/* 获取队列的长度 */
func size() -> Int {
_size
}
/* 判断队列是否为空 */
func isEmpty() -> Bool {
size() == 0
}
/* 入队 */
func push(num: Int) {
// 尾结点后添加 num
let node = ListNode(x: num)
// 如果队列为空,则令头、尾结点都指向该结点
if front == nil {
front = node
rear = node
}
// 如果队列不为空,则将该结点添加到尾结点后
else {
rear?.next = node
rear = node
}
_size += 1
}
/* 出队 */
@discardableResult
func pop() -> Int {
let num = peek()
// 删除头结点
front = front?.next
_size -= 1
return num
}
/* 访问队首元素 */
func peek() -> Int {
if isEmpty() {
fatalError("队列为空")
}
return front!.val
}
/* 将链表转化为 Array 并返回 */
func toArray() -> [Int] {
var node = front
var res = Array(repeating: 0, count: size())
for i in res.indices {
res[i] = node!.val
node = node?.next
}
return res
}
}
```
=== "Zig"
```zig title="linkedlist_queue.zig"
// 基于链表实现的队列
fn LinkedListQueue(comptime T: type) type {
return struct {
const Self = @This();
front: ?*inc.ListNode(T) = null, // 头结点 front
rear: ?*inc.ListNode(T) = null, // 尾结点 rear
que_size: usize = 0, // 队列的长度
mem_arena: ?std.heap.ArenaAllocator = null,
mem_allocator: std.mem.Allocator = undefined, // 内存分配器
// 构造方法(分配内存+初始化队列)
pub fn init(self: *Self, allocator: std.mem.Allocator) !void {
if (self.mem_arena == null) {
self.mem_arena = std.heap.ArenaAllocator.init(allocator);
self.mem_allocator = self.mem_arena.?.allocator();
}
self.front = null;
self.rear = null;
self.que_size = 0;
}
// 析构方法(释放内存)
pub fn deinit(self: *Self) void {
if (self.mem_arena == null) return;
self.mem_arena.?.deinit();
}
// 获取队列的长度
pub fn size(self: *Self) usize {
return self.que_size;
}
// 判断队列是否为空
pub fn isEmpty(self: *Self) bool {
return self.size() == 0;
}
// 访问队首元素
pub fn peek(self: *Self) T {
if (self.size() == 0) @panic("队列为空");
return self.front.?.val;
}
// 入队
pub fn push(self: *Self, num: T) !void {
// 尾结点后添加 num
var node = try self.mem_allocator.create(inc.ListNode(T));
node.init(num);
// 如果队列为空,则令头、尾结点都指向该结点
if (self.front == null) {
self.front = node;
self.rear = node;
// 如果队列不为空,则将该结点添加到尾结点后
} else {
self.rear.?.next = node;
self.rear = node;
}
self.que_size += 1;
}
// 出队
pub fn pop(self: *Self) T {
var num = self.peek();
// 删除头结点
self.front = self.front.?.next;
self.que_size -= 1;
return num;
}
// 将链表转换为数组
pub fn toArray(self: *Self) ![]T {
var node = self.front;
var res = try self.mem_allocator.alloc(T, self.size());
std.mem.set(T, res, @as(T, 0));
var i: usize = 0;
while (i < res.len) : (i += 1) {
res[i] = node.?.val;
node = node.?.next;
}
return res;
}
};
}
```
### 基于数组的实现
数组的删除首元素的时间复杂度为 $O(n)$ ,这会导致出队操作效率低下。然而,我们可以采取下述的巧妙方法来避免这个问题。
考虑借助一个变量 `front` 来指向队首元素的索引,并维护变量 `queSize` 来记录队列长度。我们定义 `rear = front + queSize` ,该公式计算出来的 `rear` 指向“队尾元素索引 $+1$ ”的位置。
在该设计下,**数组中包含元素的有效区间为 `[front, rear - 1]`** ,进而
- 对于入队操作,将输入元素赋值给 `rear` 索引处,并将 `queSize` 自增 $1$ 即可;
- 对于出队操作,仅需将 `front` 自增 $1$ ,并将 `queSize` 自减 $1$ 即可;
观察发现,入队与出队操作都仅需单次操作即可完成,时间复杂度皆为 $O(1)$
=== "ArrayQueue"
![基于数组实现队列的入队出队操作](queue.assets/array_queue.png)
=== "push()"
![array_queue_push](queue.assets/array_queue_push.png)
=== "pop()"
![array_queue_pop](queue.assets/array_queue_pop.png)
细心的同学可能会发现一个问题:在不断入队与出队的过程中,`front` `rear` 都在向右移动,**在到达数组尾部后就无法继续移动了**。为解决此问题,**我们考虑将数组看作是首尾相接的**,这样的数组被称为「环形数组」。
对于环形数组,我们需要令 `front` `rear` 在越过数组尾部后,直接绕回到数组头部接续遍历。这种周期性规律可以通过「取余操作」来实现,详情请见以下代码。
=== "Java"
```java title="array_queue.java"
/* 基于环形数组实现的队列 */
class ArrayQueue {
private int[] nums; // 用于存储队列元素的数组
private int front; // 队首指针,指向队首元素
private int queSize; // 队列长度
public ArrayQueue(int capacity) {
nums = new int[capacity];
front = queSize = 0;
}
/* 获取队列的容量 */
public int capacity() {
return nums.length;
}
/* 获取队列的长度 */
public int size() {
return queSize;
}
/* 判断队列是否为空 */
public boolean isEmpty() {
return queSize == 0;
}
/* 入队 */
public void push(int num) {
if (queSize == capacity()) {
System.out.println("队列已满");
return;
}
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
int rear = (front + queSize) % capacity();
// num 添加至队尾
nums[rear] = num;
queSize++;
}
/* 出队 */
public int pop() {
int num = peek();
// 队首指针向后移动一位,若越过尾部则返回到数组头部
front = (front + 1) % capacity();
queSize--;
return num;
}
/* 访问队首元素 */
public int peek() {
if (isEmpty())
throw new EmptyStackException();
return nums[front];
}
/* 返回数组 */
public int[] toArray() {
// 仅转换有效长度范围内的列表元素
int[] res = new int[queSize];
for (int i = 0, j = front; i < queSize; i++, j++) {
res[i] = nums[j % capacity()];
}
return res;
}
}
```
=== "C++"
```cpp title="array_queue.cpp"
/* 基于环形数组实现的队列 */
class ArrayQueue {
private:
int *nums; // 用于存储队列元素的数组
int front; // 队首指针,指向队首元素
int queSize; // 队列长度
int queCapacity; // 队列容量
public:
ArrayQueue(int capacity) {
// 初始化数组
nums = new int[capacity];
queCapacity = capacity;
front = queSize = 0;
}
~ArrayQueue() {
delete[] nums;
}
/* 获取队列的容量 */
int capacity() {
return queCapacity;
}
/* 获取队列的长度 */
int size() {
return queSize;
}
/* 判断队列是否为空 */
bool empty() {
return size() == 0;
}
/* 入队 */
void push(int num) {
if (queSize == queCapacity) {
cout << "队列已满" << endl;
return;
}
// 计算队尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
int rear = (front + queSize) % queCapacity;
// num 添加至队尾
nums[rear] = num;
queSize++;
}
/* 出队 */
void pop() {
int num = peek();
// 队首指针向后移动一位,若越过尾部则返回到数组头部
front = (front + 1) % queCapacity;
queSize--;
}
/* 访问队首元素 */
int peek() {
if (empty())
throw out_of_range("队列为空");
return nums[front];
}
/* 将数组转化为 Vector 并返回 */
vector<int> toVector() {
// 仅转换有效长度范围内的列表元素
vector<int> arr(queSize);
for (int i = 0, j = front; i < queSize; i++, j++) {
arr[i] = nums[j % queCapacity];
}
return arr;
}
};
```
=== "Python"
```python title="array_queue.py"
class ArrayQueue:
""" 基于环形数组实现的队列 """
def __init__(self, size: int) -> None:
""" 构造方法 """
self.__nums: List[int] = [0] * size # 用于存储队列元素的数组
self.__front: int = 0 # 队首指针,指向队首元素
self.__size: int = 0 # 队列长度
def capacity(self) -> int:
""" 获取队列的容量 """
return len(self.__nums)
def size(self) -> int:
""" 获取队列的长度 """
return self.__size
def is_empty(self) -> bool:
""" 判断队列是否为空 """
return self.__size == 0
def push(self, num: int) -> None:
""" 入队 """
assert self.__size < self.capacity(), "队列已满"
# 计算尾指针,指向队尾索引 + 1
# 通过取余操作,实现 rear 越过数组尾部后回到头部
rear: int = (self.__front + self.__size) % self.capacity()
# num 添加至队尾
self.__nums[rear] = num
self.__size += 1
def pop(self) -> int:
""" 出队 """
num: int = self.peek()
# 队首指针向后移动一位,若越过尾部则返回到数组头部
self.__front = (self.__front + 1) % self.capacity()
self.__size -= 1
return num
def peek(self) -> int:
""" 访问队首元素 """
assert not self.is_empty(), "队列为空"
return self.__nums[self.__front]
def to_list(self) -> List[int]:
""" 返回列表用于打印 """
res: List[int] = [0] * self.size()
j: int = self.__front
for i in range(self.size()):
res[i] = self.__nums[(j % self.capacity())]
j += 1
return res
```
=== "Go"
```go title="array_queue.go"
/* 基于环形数组实现的队列 */
type arrayQueue struct {
nums []int // 用于存储队列元素的数组
front int // 队首指针,指向队首元素
queSize int // 队列长度
queCapacity int // 队列容量(即最大容纳元素数量)
}
/* 初始化队列 */
func newArrayQueue(queCapacity int) *arrayQueue {
return &arrayQueue{
nums: make([]int, queCapacity),
queCapacity: queCapacity,
front: 0,
queSize: 0,
}
}
/* 获取队列的长度 */
func (q *arrayQueue) size() int {
return q.queSize
}
/* 判断队列是否为空 */
func (q *arrayQueue) isEmpty() bool {
return q.queSize == 0
}
/* 入队 */
func (q *arrayQueue) push(num int) {
// 当 rear == queCapacity 表示队列已满
if q.queSize == q.queCapacity {
return
}
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
rear := (q.front + q.queSize) % q.queCapacity
// 将 num 添加至队尾
q.nums[rear] = num
q.queSize++
}
/* 出队 */
func (q *arrayQueue) pop() any {
num := q.peek()
// 队首指针向后移动一位,若越过尾部则返回到数组头部
q.front = (q.front + 1) % q.queCapacity
q.queSize--
return num
}
/* 访问队首元素 */
func (q *arrayQueue) peek() any {
if q.isEmpty() {
return nil
}
return q.nums[q.front]
}
/* 获取 Slice 用于打印 */
func (q *arrayQueue) toSlice() []int {
rear := (q.front + q.queSize)
if rear >= q.queCapacity {
rear %= q.queCapacity
return append(q.nums[q.front:], q.nums[:rear]...)
}
return q.nums[q.front:rear]
}
```
=== "JavaScript"
```javascript title="array_queue.js"
/* 基于环形数组实现的队列 */
class ArrayQueue {
#nums; // 用于存储队列元素的数组
#front = 0; // 队首指针,指向队首元素
#queSize = 0; // 队列长度
constructor(capacity) {
this.#nums = new Array(capacity);
}
/* 获取队列的容量 */
get capacity() {
return this.#nums.length;
}
/* 获取队列的长度 */
get size() {
return this.#queSize;
}
/* 判断队列是否为空 */
empty() {
return this.#queSize == 0;
}
/* 入队 */
push(num) {
if (this.size == this.capacity) {
console.log("队列已满");
return;
}
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
const rear = (this.#front + this.size) % this.capacity;
// 将 num 添加至队尾
this.#nums[rear] = num;
this.#queSize++;
}
/* 出队 */
pop() {
const num = this.peek();
// 队首指针向后移动一位,若越过尾部则返回到数组头部
this.#front = (this.#front + 1) % this.capacity;
this.#queSize--;
return num;
}
/* 访问队首元素 */
peek() {
if (this.empty())
throw new Error("队列为空");
return this.#nums[this.#front];
}
/* 返回 Array */
toArray() {
// 仅转换有效长度范围内的列表元素
const arr = new Array(this.size);
for (let i = 0, j = this.#front; i < this.size; i++, j++) {
arr[i] = this.#nums[j % this.capacity];
}
return arr;
}
}
```
=== "TypeScript"
```typescript title="array_queue.ts"
/* 基于环形数组实现的队列 */
class ArrayQueue {
private nums: number[]; // 用于存储队列元素的数组
private front: number; // 队首指针,指向队首元素
private queSize: number; // 队列长度
constructor(capacity: number) {
this.nums = new Array(capacity);
this.front = this.queSize = 0;
}
/* 获取队列的容量 */
get capacity(): number {
return this.nums.length;
}
/* 获取队列的长度 */
get size(): number {
return this.queSize;
}
/* 判断队列是否为空 */
empty(): boolean {
return this.queSize == 0;
}
/* 入队 */
push(num: number): void {
if (this.size == this.capacity) {
console.log("队列已满");
return;
}
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
const rear = (this.front + this.queSize) % this.capacity;
// num 添加至队尾
this.nums[rear] = num;
this.queSize++;
}
/* 出队 */
pop(): number {
const num = this.peek();
// 队首指针向后移动一位,若越过尾部则返回到数组头部
this.front = (this.front + 1) % this.capacity;
this.queSize--;
return num;
}
/* 访问队首元素 */
peek(): number {
if (this.empty())
throw new Error("队列为空");
return this.nums[this.front];
}
/* 返回 Array */
toArray(): number[] {
// 仅转换有效长度范围内的列表元素
const arr = new Array(this.size);
for (let i = 0, j = this.front; i < this.size; i++, j++) {
arr[i] = this.nums[j % this.capacity];
}
return arr;
}
}
```
=== "C"
```c title="array_queue.c"
[class]{arrayQueue}-[func]{}
```
=== "C#"
```csharp title="array_queue.cs"
/* 基于环形数组实现的队列 */
class ArrayQueue
{
private int[] nums; // 用于存储队列元素的数组
private int front; // 队首指针,指向队首元素
private int queSize; // 队列长度
public ArrayQueue(int capacity)
{
nums = new int[capacity];
front = queSize = 0;
}
/* 获取队列的容量 */
public int capacity()
{
return nums.Length;
}
/* 获取队列的长度 */
public int size()
{
return queSize;
}
/* 判断队列是否为空 */
public bool isEmpty()
{
return queSize == 0;
}
/* 入队 */
public void push(int num)
{
if (queSize == capacity())
{
Console.WriteLine("队列已满");
return;
}
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
int rear = (front + queSize) % capacity();
// num 添加至队尾
nums[rear] = num;
queSize++;
}
/* 出队 */
public int pop()
{
int num = peek();
// 队首指针向后移动一位,若越过尾部则返回到数组头部
front = (front + 1) % capacity();
queSize--;
return num;
}
/* 访问队首元素 */
public int peek()
{
if (isEmpty())
throw new Exception();
return nums[front];
}
/* 返回数组 */
public int[] toArray()
{
// 仅转换有效长度范围内的列表元素
int[] res = new int[queSize];
for (int i = 0, j = front; i < queSize; i++, j++)
{
res[i] = nums[j % this.capacity()];
}
return res;
}
}
```
=== "Swift"
```swift title="array_queue.swift"
/* 基于环形数组实现的队列 */
class ArrayQueue {
private var nums: [Int] // 用于存储队列元素的数组
private var front = 0 // 队首指针,指向队首元素
private var queSize = 0 // 队列长度
init(capacity: Int) {
// 初始化数组
nums = Array(repeating: 0, count: capacity)
}
/* 获取队列的容量 */
func capacity() -> Int {
nums.count
}
/* 获取队列的长度 */
func size() -> Int {
queSize
}
/* 判断队列是否为空 */
func isEmpty() -> Bool {
queSize == 0
}
/* 入队 */
func push(num: Int) {
if size() == capacity() {
print("队列已满")
return
}
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
let rear = (front + queSize) % capacity()
// 将 num 添加至队尾
nums[rear] = num
queSize += 1
}
/* 出队 */
@discardableResult
func pop() -> Int {
let num = peek()
// 队首指针向后移动一位,若越过尾部则返回到数组头部
front = (front + 1) % capacity()
queSize -= 1
return num
}
/* 访问队首元素 */
func peek() -> Int {
if isEmpty() {
fatalError("队列为空")
}
return nums[front]
}
/* 返回数组 */
func toArray() -> [Int] {
// 仅转换有效长度范围内的列表元素
var res = Array(repeating: 0, count: queSize)
for (i, j) in sequence(first: (0, front), next: { $0 < self.queSize - 1 ? ($0 + 1, $1 + 1) : nil }) {
res[i] = nums[j % capacity()]
}
return res
}
}
```
=== "Zig"
```zig title="array_queue.zig"
// 基于环形数组实现的队列
fn ArrayQueue(comptime T: type) type {
return struct {
const Self = @This();
nums: []T = undefined, // 用于存储队列元素的数组
cap: usize = 0, // 队列容量
front: usize = 0, // 队首指针,指向队首元素
que_size: usize = 0, // 尾指针,指向队尾 + 1
mem_arena: ?std.heap.ArenaAllocator = null,
mem_allocator: std.mem.Allocator = undefined, // 内存分配器
// 构造方法(分配内存+初始化数组)
pub fn init(self: *Self, allocator: std.mem.Allocator, cap: usize) !void {
if (self.mem_arena == null) {
self.mem_arena = std.heap.ArenaAllocator.init(allocator);
self.mem_allocator = self.mem_arena.?.allocator();
}
self.cap = cap;
self.nums = try self.mem_allocator.alloc(T, self.cap);
std.mem.set(T, self.nums, @as(T, 0));
}
// 析构方法(释放内存)
pub fn deinit(self: *Self) void {
if (self.mem_arena == null) return;
self.mem_arena.?.deinit();
}
// 获取队列的容量
pub fn capacity(self: *Self) usize {
return self.cap;
}
// 获取队列的长度
pub fn size(self: *Self) usize {
return self.que_size;
}
// 判断队列是否为空
pub fn isEmpty(self: *Self) bool {
return self.que_size == 0;
}
// 入队
pub fn push(self: *Self, num: T) !void {
if (self.size() == self.capacity()) {
std.debug.print("队列已满\n", .{});
return;
}
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
var rear = (self.front + self.que_size) % self.capacity();
// num 添加至队尾
self.nums[rear] = num;
self.que_size += 1;
}
// 出队
pub fn pop(self: *Self) T {
var num = self.peek();
// 队首指针向后移动一位,若越过尾部则返回到数组头部
self.front = (self.front + 1) % self.capacity();
self.que_size -= 1;
return num;
}
// 访问队首元素
pub fn peek(self: *Self) T {
if (self.isEmpty()) @panic("队列为空");
return self.nums[self.front];
}
// 返回数组
pub fn toArray(self: *Self) ![]T {
// 仅转换有效长度范围内的列表元素
var res = try self.mem_allocator.alloc(T, self.size());
std.mem.set(T, res, @as(T, 0));
var i: usize = 0;
var j: usize = self.front;
while (i < self.size()) : ({ i += 1; j += 1; }) {
res[i] = self.nums[j % self.capacity()];
}
return res;
}
};
}
```
以上实现的队列仍存在局限性,即长度不可变。不过这个问题很容易解决,我们可以将数组替换为列表(即动态数组),从而引入扩容机制。有兴趣的同学可以尝试自行实现。
## 5.2.3. &nbsp; 两种实现对比
与栈的结论一致,在此不再赘述。
## 5.2.4. &nbsp; 队列典型应用
- **淘宝订单**。购物者下单后,订单就被加入到队列之中,随后系统再根据顺序依次处理队列中的订单。在双十一时,在短时间内会产生海量的订单,如何处理「高并发」则是工程师们需要重点思考的问题。
- **各种待办事项**。任何需要实现“先来后到”的功能,例如打印机的任务队列、餐厅的出餐队列等等。