This commit is contained in:
krahets 2023-02-16 02:20:47 +08:00
parent 02df24e874
commit a59e1f8054
2 changed files with 208 additions and 24 deletions

View File

@ -293,12 +293,16 @@ comments: true
```
## 5.3.2. 双向队列实现
## 5.3.2. 双向队列实现 *
双向队列需要一种可以在两端添加、两端删除的数据结构。与队列的实现方法类似,双向队列也可以使用双向链表和循环数组来实现。
与队列类似,双向队列同样可以使用链表或数组来实现。
### 基于双向链表的实现
回忆上节内容,由于可以方便地删除链表头结点(对应出队操作),以及在链表尾结点后添加新结点(对应入队操作),因此我们使用普通单向链表来实现队列。
而双向队列的头部和尾部都可以执行入队与出队操作,换言之,双向队列的操作是“首尾对称”的,也需要实现另一个对称方向的操作。因此,双向队列需要使用「双向链表」来实现。
我们将双向链表的头结点和尾结点分别看作双向队列的队首和队尾,并且实现在两端都能添加与删除结点。
=== "LinkedListDeque"
@ -316,7 +320,7 @@ comments: true
=== "pollFirst()"
![linkedlist_deque_poll_first](deque.assets/linkedlist_deque_poll_first.png)
以下是使用双向链表实现双向队列的示例代码。
以下是具体实现代码。
=== "Java"
@ -434,19 +438,15 @@ comments: true
return isEmpty() ? null : rear.val;
}
/* 打印双向队列 */
public void print() {
if (isEmpty()) {
System.out.println("[ ]");
return;
/* 返回数组用于打印 */
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;
}
List<String> list = new ArrayList<>();
ListNode head = front;
while (head != null) {
list.add(String.valueOf(head.val));
head = head.next;
}
System.out.println("[" + String.join(", ", list) + "]");
return res;
}
}
```
@ -748,3 +748,187 @@ comments: true
```zig title="linkedlist_deque.zig"
```
### 基于数组的实现
与基于数组实现队列类似,我们也可以使用环形数组来实现双向队列。在实现队列的基础上,增加实现“队首入队”和“队尾出队”方法即可。
=== "ArrayDeque"
![array_deque](deque.assets/array_deque.png)
=== "pushLast()"
![array_deque_push_last](deque.assets/array_deque_push_last.png)
=== "pushFirst()"
![array_deque_push_first](deque.assets/array_deque_push_first.png)
=== "pollLast()"
![array_deque_poll_last](deque.assets/array_deque_poll_last.png)
=== "pollFirst()"
![array_deque_poll_first](deque.assets/array_deque_poll_first.png)
以下是具体实现代码。
=== "Java"
```java title="array_deque.java"
/* 基于环形数组实现的双向队列 */
class ArrayDeque {
private int[] nums; // 用于存储双向队列元素的数组
private int front; // 队首指针,指向队首元素
private int queSize; // 双向队列长度
/* 构造方法 */
public ArrayDeque(int capacity) {
this.nums = new int[capacity];
front = queSize = 0;
}
/* 获取双向队列的容量 */
public int capacity() {
return nums.length;
}
/* 获取双向队列的长度 */
public int size() {
return queSize;
}
/* 判断双向队列是否为空 */
public boolean isEmpty() {
return queSize == 0;
}
/* 计算环形数组索引 */
private int index(int i) {
// 通过取余操作实现数组首尾相连
// 当 i 越过数组尾部后,回到头部
// 当 i 越过数组头部后,回到尾部
return (i + capacity()) % capacity();
}
/* 队首入队 */
public void pushFirst(int num) {
if (queSize == capacity()) {
System.out.println("双向队列已满");
return;
}
// 队首指针向左移动一位
// 通过取余操作,实现 front 越过数组头部后回到尾部
front = index(front - 1);
// 将 num 添加至队首
nums[front] = num;
queSize++;
}
/* 队尾入队 */
public void pushLast(int num) {
if (queSize == capacity()) {
System.out.println("双向队列已满");
return;
}
// 计算尾指针,指向队尾索引 + 1
int rear = index(front + queSize);
// 将 num 添加至队尾
nums[rear] = num;
queSize++;
}
/* 队首出队 */
public int pollFirst() {
int num = peekFirst();
// 队首指针向后移动一位
front = index(front + 1);
queSize--;
return num;
}
/* 队尾出队 */
public int pollLast() {
int num = peekLast();
queSize--;
return num;
}
/* 访问队首元素 */
public int peekFirst() {
if (isEmpty())
throw new EmptyStackException();
return nums[front];
}
/* 访问队尾元素 */
public int peekLast() {
if (isEmpty())
throw new EmptyStackException();
// 计算尾元素索引
int last = index(front + queSize - 1);
return nums[last];
}
/* 返回数组用于打印 */
public int[] toArray() {
// 仅转换有效长度范围内的列表元素
int[] res = new int[queSize];
for (int i = 0, j = front; i < queSize; i++, j++) {
res[i] = nums[index(j)];
}
return res;
}
}
```
=== "C++"
```cpp title="array_deque.cpp"
```
=== "Python"
```python title="array_deque.py"
```
=== "Go"
```go title="array_deque.go"
```
=== "JavaScript"
```js title="array_deque.js"
```
=== "TypeScript"
```typescript title="array_deque.ts"
```
=== "C"
```c title="array_deque.c"
```
=== "C#"
```csharp title="array_deque.cs"
```
=== "Swift"
```swift title="array_deque.swift"
```
=== "Zig"
```zig title="array_deque.zig"
```

View File

@ -983,7 +983,7 @@ comments: true
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
int rear = (front + queSize) % capacity();
// 尾结点后添加 num
// 将 num 添加至队尾
nums[rear] = num;
queSize++;
}
@ -1063,7 +1063,7 @@ comments: true
// 计算队尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
int rear = (front + queSize) % queCapacity;
// 尾结点后添加 num
// 将 num 添加至队尾
nums[rear] = num;
queSize++;
}
@ -1123,7 +1123,7 @@ comments: true
# 计算尾指针,指向队尾索引 + 1
# 通过取余操作,实现 rear 越过数组尾部后回到头部
rear = (self.__front + self.__size) % self.capacity()
# 尾结点后添加 num
# 将 num 添加至队尾
self.__nums[rear] = num
self.__size += 1
@ -1190,7 +1190,7 @@ comments: true
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
rear := (q.front + q.queSize) % q.queCapacity
// 尾结点后添加 num
// 将 num 添加至队尾
q.nums[rear] = num
q.queSize++
}
@ -1260,7 +1260,7 @@ comments: true
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
const rear = (this.#front + this.size) % this.capacity;
// 尾结点后添加 num
// 将 num 添加至队尾
this.#nums[rear] = num;
this.#queSize++;
}
@ -1331,7 +1331,7 @@ comments: true
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
const rear = (this.front + this.queSize) % this.capacity;
// 尾结点后添加 num
// 将 num 添加至队尾
this.nums[rear] = num;
this.queSize++;
}
@ -1415,7 +1415,7 @@ comments: true
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
int rear = (front + queSize) % capacity();
// 尾结点后添加 num
// 将 num 添加至队尾
nums[rear] = num;
queSize++;
}
@ -1490,7 +1490,7 @@ comments: true
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
let rear = (front + queSize) % capacity()
// 尾结点后添加 num
// 将 num 添加至队尾
nums[rear] = num
queSize += 1
}
@ -1581,7 +1581,7 @@ comments: true
// 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
var rear = (self.front + self.queSize) % self.capacity();
// 尾结点后添加 num
// 将 num 添加至队尾
self.nums[rear] = num;
self.queSize += 1;
}