Revisit the English version (#1835)

* Review the English version using Claude-4.5.

* Update mkdocs.yml

* Align the section titles.

* Bug fixes
This commit is contained in:
Yudong Jin
2025-12-30 17:54:01 +08:00
committed by GitHub
parent 091afd38b4
commit 45e1295241
106 changed files with 4195 additions and 3398 deletions
+187 -134
View File
@@ -1,320 +1,320 @@
# Double-ended queue
# Deque
In a queue, we can only delete elements from the head or add elements to the tail. As shown in the figure below, a <u>double-ended queue (deque)</u> offers more flexibility, allowing the addition or removal of elements at both the head and the tail.
In a queue, we can only remove elements from the front or add elements at the rear. As shown in the figure below, a <u>double-ended queue (deque)</u> provides greater flexibility, allowing the addition or removal of elements at both the front and rear.
![Operations in double-ended queue](deque.assets/deque_operations.png)
![Operations of deque](deque.assets/deque_operations.png)
## Common operations in double-ended queue
## Common Deque Operations
The common operations in a double-ended queue are listed below, and the names of specific methods depend on the programming language used.
The common operations on a deque are shown in the table below. The specific method names depend on the programming language used.
<p align="center"> Table <id> &nbsp; Efficiency of double-ended queue operations </p>
<p align="center"> Table <id> &nbsp; Efficiency of Deque Operations </p>
| Method Name | Description | Time Complexity |
| ------------- | -------------------------- | --------------- |
| `pushFirst()` | Add an element to the head | $O(1)$ |
| `pushLast()` | Add an element to the tail | $O(1)$ |
| `popFirst()` | Remove the first element | $O(1)$ |
| `popLast()` | Remove the last element | $O(1)$ |
| `peekFirst()` | Access the first element | $O(1)$ |
| `peekLast()` | Access the last element | $O(1)$ |
| Method | Description | Time Complexity |
| -------------- | ------------------------- | --------------- |
| `push_first()` | Add element to front | $O(1)$ |
| `push_last()` | Add element to rear | $O(1)$ |
| `pop_first()` | Remove front element | $O(1)$ |
| `pop_last()` | Remove rear element | $O(1)$ |
| `peek_first()` | Access front element | $O(1)$ |
| `peek_last()` | Access rear element | $O(1)$ |
Similarly, we can directly use the double-ended queue classes implemented in programming languages:
Similarly, we can directly use the deque classes already implemented in programming languages:
=== "Python"
```python title="deque.py"
from collections import deque
# Initialize the deque
# Initialize deque
deq: deque[int] = deque()
# Enqueue elements
deq.append(2) # Add to the tail
deq.append(2) # Add to rear
deq.append(5)
deq.append(4)
deq.appendleft(3) # Add to the head
deq.appendleft(3) # Add to front
deq.appendleft(1)
# Access elements
front: int = deq[0] # The first element
rear: int = deq[-1] # The last element
front: int = deq[0] # Front element
rear: int = deq[-1] # Rear element
# Dequeue elements
pop_front: int = deq.popleft() # The first element dequeued
pop_rear: int = deq.pop() # The last element dequeued
pop_front: int = deq.popleft() # Front element dequeue
pop_rear: int = deq.pop() # Rear element dequeue
# Get the length of the deque
# Get deque length
size: int = len(deq)
# Check if the deque is empty
# Check if deque is empty
is_empty: bool = len(deq) == 0
```
=== "C++"
```cpp title="deque.cpp"
/* Initialize the deque */
/* Initialize deque */
deque<int> deque;
/* Enqueue elements */
deque.push_back(2); // Add to the tail
deque.push_back(2); // Add to rear
deque.push_back(5);
deque.push_back(4);
deque.push_front(3); // Add to the head
deque.push_front(3); // Add to front
deque.push_front(1);
/* Access elements */
int front = deque.front(); // The first element
int back = deque.back(); // The last element
int front = deque.front(); // Front element
int back = deque.back(); // Rear element
/* Dequeue elements */
deque.pop_front(); // The first element dequeued
deque.pop_back(); // The last element dequeued
deque.pop_front(); // Front element dequeue
deque.pop_back(); // Rear element dequeue
/* Get the length of the deque */
/* Get deque length */
int size = deque.size();
/* Check if the deque is empty */
/* Check if deque is empty */
bool empty = deque.empty();
```
=== "Java"
```java title="deque.java"
/* Initialize the deque */
/* Initialize deque */
Deque<Integer> deque = new LinkedList<>();
/* Enqueue elements */
deque.offerLast(2); // Add to the tail
deque.offerLast(2); // Add to rear
deque.offerLast(5);
deque.offerLast(4);
deque.offerFirst(3); // Add to the head
deque.offerFirst(3); // Add to front
deque.offerFirst(1);
/* Access elements */
int peekFirst = deque.peekFirst(); // The first element
int peekLast = deque.peekLast(); // The last element
int peekFirst = deque.peekFirst(); // Front element
int peekLast = deque.peekLast(); // Rear element
/* Dequeue elements */
int popFirst = deque.pollFirst(); // The first element dequeued
int popLast = deque.pollLast(); // The last element dequeued
int popFirst = deque.pollFirst(); // Front element dequeue
int popLast = deque.pollLast(); // Rear element dequeue
/* Get the length of the deque */
/* Get deque length */
int size = deque.size();
/* Check if the deque is empty */
/* Check if deque is empty */
boolean isEmpty = deque.isEmpty();
```
=== "C#"
```csharp title="deque.cs"
/* Initialize the deque */
// In C#, LinkedList is used as a deque
/* Initialize deque */
// In C#, use LinkedList as a deque
LinkedList<int> deque = new();
/* Enqueue elements */
deque.AddLast(2); // Add to the tail
deque.AddLast(2); // Add to rear
deque.AddLast(5);
deque.AddLast(4);
deque.AddFirst(3); // Add to the head
deque.AddFirst(3); // Add to front
deque.AddFirst(1);
/* Access elements */
int peekFirst = deque.First.Value; // The first element
int peekLast = deque.Last.Value; // The last element
int peekFirst = deque.First.Value; // Front element
int peekLast = deque.Last.Value; // Rear element
/* Dequeue elements */
deque.RemoveFirst(); // The first element dequeued
deque.RemoveLast(); // The last element dequeued
deque.RemoveFirst(); // Front element dequeue
deque.RemoveLast(); // Rear element dequeue
/* Get the length of the deque */
/* Get deque length */
int size = deque.Count;
/* Check if the deque is empty */
/* Check if deque is empty */
bool isEmpty = deque.Count == 0;
```
=== "Go"
```go title="deque_test.go"
/* Initialize the deque */
/* Initialize deque */
// In Go, use list as a deque
deque := list.New()
/* Enqueue elements */
deque.PushBack(2) // Add to the tail
deque.PushBack(2) // Add to rear
deque.PushBack(5)
deque.PushBack(4)
deque.PushFront(3) // Add to the head
deque.PushFront(3) // Add to front
deque.PushFront(1)
/* Access elements */
front := deque.Front() // The first element
rear := deque.Back() // The last element
front := deque.Front() // Front element
rear := deque.Back() // Rear element
/* Dequeue elements */
deque.Remove(front) // The first element dequeued
deque.Remove(rear) // The last element dequeued
deque.Remove(front) // Front element dequeue
deque.Remove(rear) // Rear element dequeue
/* Get the length of the deque */
/* Get deque length */
size := deque.Len()
/* Check if the deque is empty */
/* Check if deque is empty */
isEmpty := deque.Len() == 0
```
=== "Swift"
```swift title="deque.swift"
/* Initialize the deque */
// Swift does not have a built-in deque class, so Array can be used as a deque
/* Initialize deque */
// Swift does not have a built-in deque class, can use Array as a deque
var deque: [Int] = []
/* Enqueue elements */
deque.append(2) // Add to the tail
deque.append(2) // Add to rear
deque.append(5)
deque.append(4)
deque.insert(3, at: 0) // Add to the head
deque.insert(3, at: 0) // Add to front
deque.insert(1, at: 0)
/* Access elements */
let peekFirst = deque.first! // The first element
let peekLast = deque.last! // The last element
let peekFirst = deque.first! // Front element
let peekLast = deque.last! // Rear element
/* Dequeue elements */
// Using Array, popFirst has a complexity of O(n)
let popFirst = deque.removeFirst() // The first element dequeued
let popLast = deque.removeLast() // The last element dequeued
// When using Array simulation, popFirst has O(n) complexity
let popFirst = deque.removeFirst() // Front element dequeue
let popLast = deque.removeLast() // Rear element dequeue
/* Get the length of the deque */
/* Get deque length */
let size = deque.count
/* Check if the deque is empty */
/* Check if deque is empty */
let isEmpty = deque.isEmpty
```
=== "JS"
```javascript title="deque.js"
/* Initialize the deque */
// JavaScript does not have a built-in deque, so Array is used as a deque
/* Initialize deque */
// JavaScript does not have a built-in deque, can only use Array as a deque
const deque = [];
/* Enqueue elements */
deque.push(2);
deque.push(5);
deque.push(4);
// Note that unshift() has a time complexity of O(n) as it's an array
// Please note that since it's an array, unshift() has O(n) time complexity
deque.unshift(3);
deque.unshift(1);
/* Access elements */
const peekFirst = deque[0]; // The first element
const peekLast = deque[deque.length - 1]; // The last element
const peekFirst = deque[0];
const peekLast = deque[deque.length - 1];
/* Dequeue elements */
// Note that shift() has a time complexity of O(n) as it's an array
const popFront = deque.shift(); // The first element dequeued
const popBack = deque.pop(); // The last element dequeued
// Please note that since it's an array, shift() has O(n) time complexity
const popFront = deque.shift();
const popBack = deque.pop();
/* Get the length of the deque */
/* Get deque length */
const size = deque.length;
/* Check if the deque is empty */
/* Check if deque is empty */
const isEmpty = size === 0;
```
=== "TS"
```typescript title="deque.ts"
/* Initialize the deque */
// TypeScript does not have a built-in deque, so Array is used as a deque
/* Initialize deque */
// TypeScript does not have a built-in deque, can only use Array as a deque
const deque: number[] = [];
/* Enqueue elements */
deque.push(2);
deque.push(5);
deque.push(4);
// Note that unshift() has a time complexity of O(n) as it's an array
// Please note that since it's an array, unshift() has O(n) time complexity
deque.unshift(3);
deque.unshift(1);
/* Access elements */
const peekFirst: number = deque[0]; // The first element
const peekLast: number = deque[deque.length - 1]; // The last element
const peekFirst: number = deque[0];
const peekLast: number = deque[deque.length - 1];
/* Dequeue elements */
// Note that shift() has a time complexity of O(n) as it's an array
const popFront: number = deque.shift() as number; // The first element dequeued
const popBack: number = deque.pop() as number; // The last element dequeued
// Please note that since it's an array, shift() has O(n) time complexity
const popFront: number = deque.shift() as number;
const popBack: number = deque.pop() as number;
/* Get the length of the deque */
/* Get deque length */
const size: number = deque.length;
/* Check if the deque is empty */
/* Check if deque is empty */
const isEmpty: boolean = size === 0;
```
=== "Dart"
```dart title="deque.dart"
/* Initialize the deque */
/* Initialize deque */
// In Dart, Queue is defined as a deque
Queue<int> deque = Queue<int>();
/* Enqueue elements */
deque.addLast(2); // Add to the tail
deque.addLast(2); // Add to rear
deque.addLast(5);
deque.addLast(4);
deque.addFirst(3); // Add to the head
deque.addFirst(3); // Add to front
deque.addFirst(1);
/* Access elements */
int peekFirst = deque.first; // The first element
int peekLast = deque.last; // The last element
int peekFirst = deque.first; // Front element
int peekLast = deque.last; // Rear element
/* Dequeue elements */
int popFirst = deque.removeFirst(); // The first element dequeued
int popLast = deque.removeLast(); // The last element dequeued
int popFirst = deque.removeFirst(); // Front element dequeue
int popLast = deque.removeLast(); // Rear element dequeue
/* Get the length of the deque */
/* Get deque length */
int size = deque.length;
/* Check if the deque is empty */
/* Check if deque is empty */
bool isEmpty = deque.isEmpty;
```
=== "Rust"
```rust title="deque.rs"
/* Initialize the deque */
/* Initialize deque */
let mut deque: VecDeque<u32> = VecDeque::new();
/* Enqueue elements */
deque.push_back(2); // Add to the tail
deque.push_back(2); // Add to rear
deque.push_back(5);
deque.push_back(4);
deque.push_front(3); // Add to the head
deque.push_front(3); // Add to front
deque.push_front(1);
/* Access elements */
if let Some(front) = deque.front() { // The first element
if let Some(front) = deque.front() { // Front element
}
if let Some(rear) = deque.back() { // The last element
if let Some(rear) = deque.back() { // Rear element
}
/* Dequeue elements */
if let Some(pop_front) = deque.pop_front() { // The first element dequeued
if let Some(pop_front) = deque.pop_front() { // Front element dequeue
}
if let Some(pop_rear) = deque.pop_back() { // The last element dequeued
if let Some(pop_rear) = deque.pop_back() { // Rear element dequeue
}
/* Get the length of the deque */
/* Get deque length */
let size = deque.len();
/* Check if the deque is empty */
/* Check if deque is empty */
let is_empty = deque.is_empty();
```
@@ -327,7 +327,60 @@ Similarly, we can directly use the double-ended queue classes implemented in pro
=== "Kotlin"
```kotlin title="deque.kt"
/* Initialize deque */
val deque = LinkedList<Int>()
/* Enqueue elements */
deque.offerLast(2) // Add to rear
deque.offerLast(5)
deque.offerLast(4)
deque.offerFirst(3) // Add to front
deque.offerFirst(1)
/* Access elements */
val peekFirst = deque.peekFirst() // Front element
val peekLast = deque.peekLast() // Rear element
/* Dequeue elements */
val popFirst = deque.pollFirst() // Front element dequeue
val popLast = deque.pollLast() // Rear element dequeue
/* Get deque length */
val size = deque.size
/* Check if deque is empty */
val isEmpty = deque.isEmpty()
```
=== "Ruby"
```ruby title="deque.rb"
# Initialize deque
# Ruby does not have a built-in deque, can only use Array as a deque
deque = []
# Enqueue elements
deque << 2
deque << 5
deque << 4
# Please note that since it's an array, Array#unshift has O(n) time complexity
deque.unshift(3)
deque.unshift(1)
# Access elements
peek_first = deque.first
peek_last = deque.last
# Dequeue elements
# Please note that since it's an array, Array#shift has O(n) time complexity
pop_front = deque.shift
pop_back = deque.pop
# Get deque length
size = deque.length
# Check if deque is empty
is_empty = size.zero?
```
=== "Zig"
@@ -336,70 +389,70 @@ Similarly, we can directly use the double-ended queue classes implemented in pro
```
??? pythontutor "Visualizing Code"
??? pythontutor "Visualize Execution"
https://pythontutor.com/render.html#code=from%20collections%20import%20deque%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%0A%20%20%20%20deq%20%3D%20deque%28%29%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%85%A5%E9%98%9F%0A%20%20%20%20deq.append%282%29%20%20%23%20%E6%B7%BB%E5%8A%A0%E8%87%B3%E9%98%9F%E5%B0%BE%0A%20%20%20%20deq.append%285%29%0A%20%20%20%20deq.append%284%29%0A%20%20%20%20deq.appendleft%283%29%20%20%23%20%E6%B7%BB%E5%8A%A0%E8%87%B3%E9%98%9F%E9%A6%96%0A%20%20%20%20deq.appendleft%281%29%0A%20%20%20%20print%28%22%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%20deque%20%3D%22,%20deq%29%0A%0A%20%20%20%20%23%20%E8%AE%BF%E9%97%AE%E5%85%83%E7%B4%A0%0A%20%20%20%20front%20%3D%20deq%5B0%5D%20%20%23%20%E9%98%9F%E9%A6%96%E5%85%83%E7%B4%A0%0A%20%20%20%20print%28%22%E9%98%9F%E9%A6%96%E5%85%83%E7%B4%A0%20front%20%3D%22,%20front%29%0A%20%20%20%20rear%20%3D%20deq%5B-1%5D%20%20%23%20%E9%98%9F%E5%B0%BE%E5%85%83%E7%B4%A0%0A%20%20%20%20print%28%22%E9%98%9F%E5%B0%BE%E5%85%83%E7%B4%A0%20rear%20%3D%22,%20rear%29%0A%0A%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E5%87%BA%E9%98%9F%0A%20%20%20%20pop_front%20%3D%20deq.popleft%28%29%20%20%23%20%E9%98%9F%E9%A6%96%E5%85%83%E7%B4%A0%E5%87%BA%E9%98%9F%0A%20%20%20%20print%28%22%E9%98%9F%E9%A6%96%E5%87%BA%E9%98%9F%E5%85%83%E7%B4%A0%20%20pop_front%20%3D%22,%20pop_front%29%0A%20%20%20%20print%28%22%E9%98%9F%E9%A6%96%E5%87%BA%E9%98%9F%E5%90%8E%20deque%20%3D%22,%20deq%29%0A%20%20%20%20pop_rear%20%3D%20deq.pop%28%29%20%20%23%20%E9%98%9F%E5%B0%BE%E5%85%83%E7%B4%A0%E5%87%BA%E9%98%9F%0A%20%20%20%20print%28%22%E9%98%9F%E5%B0%BE%E5%87%BA%E9%98%9F%E5%85%83%E7%B4%A0%20%20pop_rear%20%3D%22,%20pop_rear%29%0A%20%20%20%20print%28%22%E9%98%9F%E5%B0%BE%E5%87%BA%E9%98%9F%E5%90%8E%20deque%20%3D%22,%20deq%29%0A%0A%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E7%9A%84%E9%95%BF%E5%BA%A6%0A%20%20%20%20size%20%3D%20len%28deq%29%0A%20%20%20%20print%28%22%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E9%95%BF%E5%BA%A6%20size%20%3D%22,%20size%29%0A%0A%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%0A%20%20%20%20is_empty%20%3D%20len%28deq%29%20%3D%3D%200%0A%20%20%20%20print%28%22%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97%E6%98%AF%E5%90%A6%E4%B8%BA%E7%A9%BA%20%3D%22,%20is_empty%29&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
## Implementing a double-ended queue *
## Deque Implementation *
The implementation of a double-ended queue is similar to that of a regular queue, it can be based on either a linked list or an array as the underlying data structure.
The implementation of a deque is similar to that of a queue. You can choose either a linked list or an array as the underlying data structure.
### Implementation based on doubly linked list
### Doubly Linked List Implementation
Recall from the previous section that we used a regular singly linked list to implement a queue, as it conveniently allows for deleting from the head (corresponding to the dequeue operation) and adding new elements after the tail (corresponding to the enqueue operation).
Reviewing the previous section, we used a regular singly linked list to implement a queue because it conveniently allows deleting the head node (corresponding to dequeue) and adding new nodes after the tail node (corresponding to enqueue).
For a double-ended queue, both the head and the tail can perform enqueue and dequeue operations. In other words, a double-ended queue needs to implement operations in the opposite direction as well. For this, we use a "doubly linked list" as the underlying data structure of the double-ended queue.
For a deque, both the front and rear can perform enqueue and dequeue operations. In other words, a deque needs to implement operations in the opposite direction as well. For this reason, we use a "doubly linked list" as the underlying data structure for the deque.
As shown in the figure below, we treat the head and tail nodes of the doubly linked list as the front and rear of the double-ended queue, respectively, and implement the functionality to add and remove nodes at both ends.
As shown in the figure below, we treat the head and tail nodes of the doubly linked list as the front and rear of the deque, implementing functionality to add and remove nodes at both ends.
=== "LinkedListDeque"
![Implementing Double-Ended Queue with Doubly Linked List for Enqueue and Dequeue Operations](deque.assets/linkedlist_deque_step1.png)
![Enqueue and dequeue operations in linked list implementation of deque](deque.assets/linkedlist_deque_step1.png)
=== "pushLast()"
=== "push_last()"
![linkedlist_deque_push_last](deque.assets/linkedlist_deque_step2_push_last.png)
=== "pushFirst()"
=== "push_first()"
![linkedlist_deque_push_first](deque.assets/linkedlist_deque_step3_push_first.png)
=== "popLast()"
=== "pop_last()"
![linkedlist_deque_pop_last](deque.assets/linkedlist_deque_step4_pop_last.png)
=== "popFirst()"
=== "pop_first()"
![linkedlist_deque_pop_first](deque.assets/linkedlist_deque_step5_pop_first.png)
The implementation code is as follows:
The implementation code is shown below:
```src
[file]{linkedlist_deque}-[class]{linked_list_deque}-[func]{}
```
### Implementation based on array
### Array Implementation
As shown in the figure below, similar to implementing a queue with an array, we can also use a circular array to implement a double-ended queue.
As shown in the figure below, similar to implementing a queue based on an array, we can also use a circular array to implement a deque.
=== "ArrayDeque"
![Implementing Double-Ended Queue with Array for Enqueue and Dequeue Operations](deque.assets/array_deque_step1.png)
![Enqueue and dequeue operations in array implementation of deque](deque.assets/array_deque_step1.png)
=== "pushLast()"
=== "push_last()"
![array_deque_push_last](deque.assets/array_deque_step2_push_last.png)
=== "pushFirst()"
=== "push_first()"
![array_deque_push_first](deque.assets/array_deque_step3_push_first.png)
=== "popLast()"
=== "pop_last()"
![array_deque_pop_last](deque.assets/array_deque_step4_pop_last.png)
=== "popFirst()"
=== "pop_first()"
![array_deque_pop_first](deque.assets/array_deque_step5_pop_first.png)
The implementation only needs to add methods for "front enqueue" and "rear dequeue":
Based on the queue implementation, we only need to add methods for "enqueue at front" and "dequeue from rear":
```src
[file]{array_deque}-[class]{array_deque}-[func]{}
```
## Applications of double-ended queue
## Deque Applications
The double-ended queue combines the logic of both stacks and queues, **thus, it can implement all their respective use cases while offering greater flexibility**.
A deque combines the logic of both stacks and queues. **Therefore, it can implement all application scenarios of both, while providing greater flexibility**.
We know that software's "undo" feature is typically implemented using a stack: the system `pushes` each change operation onto the stack and then `pops` to implement undoing. However, considering the limitations of system resources, software often restricts the number of undo steps (for example, only allowing the last 50 steps). When the stack length exceeds 50, the software needs to perform a deletion operation at the bottom of the stack (the front of the queue). **But a regular stack cannot perform this function, where a double-ended queue becomes necessary**. Note that the core logic of "undo" still follows the Last-In-First-Out principle of a stack, but a double-ended queue can more flexibly implement some additional logic.
We know that the "undo" function in software is typically implemented using a stack: the system pushes each change operation onto the stack and then implements undo through pop. However, considering system resource limitations, software usually limits the number of undo steps (for example, only allowing 50 steps to be saved). When the stack length exceeds 50, the software needs to perform a deletion operation at the bottom of the stack (front of the queue). **But a stack cannot implement this functionality, so a deque is needed to replace the stack**. Note that the core logic of "undo" still follows the LIFO principle of a stack; it's just that the deque can more flexibly implement some additional logic.