feat: add ArrayDeque (#348)

* 双向队列: java 代码

* 双向队列: markdown 内容

* Rewrite array_deque.java
Update array_queue.java, linkedlist_deque.java

* Add ArrayDeque figures and rewrite the contents

---------

Co-authored-by: krahets <krahets@163.com>
This commit is contained in:
方圆 2023-02-16 02:17:15 +08:00 committed by GitHub
parent 0fa78e4b3b
commit f0d8d8b1b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 262 additions and 35 deletions

View file

@ -62,7 +62,7 @@ void push(ArrayQueue *queue, int num) {
// 计算队尾指针,指向队尾索引 + 1 // 计算队尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部 // 通过取余操作,实现 rear 越过数组尾部后回到头部
int rear = (queue->front + queue->queSize) % queue->queCapacity; int rear = (queue->front + queue->queSize) % queue->queCapacity;
// 尾结点后添加 num // 将 num 添加至队尾
queue->nums[rear] = num; queue->nums[rear] = num;
queue->queSize++; queue->queSize++;
} }

View file

@ -50,7 +50,7 @@ public:
// 计算队尾指针,指向队尾索引 + 1 // 计算队尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部 // 通过取余操作,实现 rear 越过数组尾部后回到头部
int rear = (front + queSize) % queCapacity; int rear = (front + queSize) % queCapacity;
// 尾结点后添加 num // 将 num 添加至队尾
nums[rear] = num; nums[rear] = num;
queSize++; queSize++;
} }

View file

@ -50,7 +50,7 @@ class ArrayQueue
// 计算尾指针指向队尾索引 + 1 // 计算尾指针指向队尾索引 + 1
// 通过取余操作实现 rear 越过数组尾部后回到头部 // 通过取余操作实现 rear 越过数组尾部后回到头部
int rear = (front + queSize) % capacity(); int rear = (front + queSize) % capacity();
// 尾结点后添加 num // num 添加至队尾
nums[rear] = num; nums[rear] = num;
queSize++; queSize++;
} }

View file

@ -41,7 +41,7 @@ func (q *arrayQueue) push(num int) {
// 计算尾指针,指向队尾索引 + 1 // 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部 // 通过取余操作,实现 rear 越过数组尾部后回到头部
rear := (q.front + q.queSize) % q.queCapacity rear := (q.front + q.queSize) % q.queCapacity
// 尾结点后添加 num // 将 num 添加至队尾
q.nums[rear] = num q.nums[rear] = num
q.queSize++ q.queSize++
} }

View file

@ -0,0 +1,151 @@
/**
* File: array_deque.java
* Created Time: 2023-02-16
* Author: Krahets (krahets@163.com), FangYuan33 (374072213@qq.com)
*/
package chapter_stack_and_queue;
import java.util.*;
/* 基于环形数组实现的双向队列 */
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;
}
}
public class array_deque {
public static void main(String[] args) {
/* 初始化双向队列 */
LinkedListDeque deque = new LinkedListDeque();
deque.pushLast(3);
deque.pushLast(2);
deque.pushLast(5);
System.out.println("双向队列 deque = " + Arrays.toString(deque.toArray()));
/* 访问元素 */
int peekFirst = deque.peekFirst();
System.out.println("队首元素 peekFirst = " + peekFirst);
int peekLast = deque.peekLast();
System.out.println("队尾元素 peekLast = " + peekLast);
/* 元素入队 */
deque.pushLast(4);
System.out.println("元素 4 队尾入队后 deque = " + Arrays.toString(deque.toArray()));
deque.pushFirst(1);
System.out.println("元素 1 队首入队后 deque = " + Arrays.toString(deque.toArray()));
/* 元素出队 */
int pollLast = deque.pollLast();
System.out.println("队尾出队元素 = " + pollLast + ",队尾出队后 deque = " + Arrays.toString(deque.toArray()));
int pollFirst = deque.pollFirst();
System.out.println("队首出队元素 = " + pollFirst + ",队首出队后 deque = " + Arrays.toString(deque.toArray()));
/* 获取双向队列的长度 */
int size = deque.size();
System.out.println("双向队列长度 size = " + size);
/* 判断双向队列是否为空 */
boolean isEmpty = deque.isEmpty();
System.out.println("双向队列是否为空 = " + isEmpty);
}
}

View file

@ -43,7 +43,7 @@ class ArrayQueue {
// 计算尾指针指向队尾索引 + 1 // 计算尾指针指向队尾索引 + 1
// 通过取余操作实现 rear 越过数组尾部后回到头部 // 通过取余操作实现 rear 越过数组尾部后回到头部
int rear = (front + queSize) % capacity(); int rear = (front + queSize) % capacity();
// 尾结点后添加 num // num 添加至队尾
nums[rear] = num; nums[rear] = num;
queSize++; queSize++;
} }

View file

@ -121,19 +121,15 @@ class LinkedListDeque {
return isEmpty() ? null : rear.val; return isEmpty() ? null : rear.val;
} }
/* 打印双向队列 */ /* 返回数组用于打印 */
public void print() { public int[] toArray() {
if (isEmpty()) { ListNode node = front;
System.out.println("[ ]"); int[] res = new int[size()];
return; for (int i = 0; i < res.length; i++) {
res[i] = node.val;
node = node.next;
} }
List<String> list = new ArrayList<>(); return res;
ListNode head = front;
while (head != null) {
list.add(String.valueOf(head.val));
head = head.next;
}
System.out.println("[" + String.join(", ", list) + "]");
} }
} }
@ -144,8 +140,7 @@ public class linkedlist_deque {
deque.pushLast(3); deque.pushLast(3);
deque.pushLast(2); deque.pushLast(2);
deque.pushLast(5); deque.pushLast(5);
System.out.print("双向队列 deque = "); System.out.println("双向队列 deque = " + Arrays.toString(deque.toArray()));
deque.print();
/* 访问元素 */ /* 访问元素 */
int peekFirst = deque.peekFirst(); int peekFirst = deque.peekFirst();
@ -155,19 +150,15 @@ public class linkedlist_deque {
/* 元素入队 */ /* 元素入队 */
deque.pushLast(4); deque.pushLast(4);
System.out.print("元素 4 队尾入队后 deque = "); System.out.println("元素 4 队尾入队后 deque = " + Arrays.toString(deque.toArray()));
deque.print();
deque.pushFirst(1); deque.pushFirst(1);
System.out.print("元素 1 队首入队后 deque = "); System.out.println("元素 1 队首入队后 deque = " + Arrays.toString(deque.toArray()));
deque.print();
/* 元素出队 */ /* 元素出队 */
int pollLast = deque.pollLast(); int pollLast = deque.pollLast();
System.out.print("队尾出队元素 = " + pollLast + ",队尾出队后 deque = "); System.out.println("队尾出队元素 = " + pollLast + ",队尾出队后 deque = " + Arrays.toString(deque.toArray()));
deque.print();
int pollFirst = deque.pollFirst(); int pollFirst = deque.pollFirst();
System.out.print("队首出队元素 = " + pollFirst + ",队首出队后 deque = "); System.out.println("队首出队元素 = " + pollFirst + ",队首出队后 deque = " + Arrays.toString(deque.toArray()));
deque.print();
/* 获取双向队列的长度 */ /* 获取双向队列的长度 */
int size = deque.size(); int size = deque.size();

View file

@ -38,7 +38,7 @@ class ArrayQueue {
// 计算尾指针,指向队尾索引 + 1 // 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部 // 通过取余操作,实现 rear 越过数组尾部后回到头部
const rear = (this.#front + this.size) % this.capacity; const rear = (this.#front + this.size) % this.capacity;
// 尾结点后添加 num // 将 num 添加至队尾
this.#nums[rear] = num; this.#nums[rear] = num;
this.#queSize++; this.#queSize++;
} }

View file

@ -35,7 +35,7 @@ class ArrayQueue:
# 计算尾指针,指向队尾索引 + 1 # 计算尾指针,指向队尾索引 + 1
# 通过取余操作,实现 rear 越过数组尾部后回到头部 # 通过取余操作,实现 rear 越过数组尾部后回到头部
rear = (self.__front + self.__size) % self.capacity() rear = (self.__front + self.__size) % self.capacity()
# 尾结点后添加 num # 将 num 添加至队尾
self.__nums[rear] = num self.__nums[rear] = num
self.__size += 1 self.__size += 1

View file

@ -39,7 +39,7 @@ class ArrayQueue {
// + 1 // + 1
// rear // rear
let rear = (front + queSize) % capacity() let rear = (front + queSize) % capacity()
// num // num
nums[rear] = num nums[rear] = num
queSize += 1 queSize += 1
} }

View file

@ -39,7 +39,7 @@ class ArrayQueue {
// 计算尾指针,指向队尾索引 + 1 // 计算尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部 // 通过取余操作,实现 rear 越过数组尾部后回到头部
const rear = (this.front + this.queSize) % this.capacity; const rear = (this.front + this.queSize) % this.capacity;
// 尾结点后添加 num // 将 num 添加至队尾
this.nums[rear] = num; this.nums[rear] = num;
this.queSize++; this.queSize++;
} }

View file

@ -58,7 +58,7 @@ pub fn ArrayQueue(comptime T: type) type {
// + 1 // + 1
// rear // rear
var rear = (self.front + self.queSize) % self.capacity(); var rear = (self.front + self.queSize) % self.capacity();
// num // num
self.nums[rear] = num; self.nums[rear] = num;
self.queSize += 1; self.queSize += 1;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View file

@ -293,12 +293,16 @@ comments: true
``` ```
## 5.3.2. 双向队列实现 ## 5.3.2. 双向队列实现 *
双向队列需要一种可以在两端添加、两端删除的数据结构。与队列的实现方法类似,双向队列也可以使用双向链表和循环数组来实现。 与队列类似,双向队列同样可以使用链表或数组来实现。
### 基于双向链表的实现 ### 基于双向链表的实现
回忆上节内容,由于可以方便地删除链表头结点(对应出队操作),以及在链表尾结点后添加新结点(对应入队操作),因此我们使用普通单向链表来实现队列。
而双向队列的头部和尾部都可以执行入队与出队操作,换言之,双向队列的操作是“首尾对称”的,也需要实现另一个对称方向的操作。因此,双向队列需要使用「双向链表」来实现。
我们将双向链表的头结点和尾结点分别看作双向队列的队首和队尾,并且实现在两端都能添加与删除结点。 我们将双向链表的头结点和尾结点分别看作双向队列的队首和队尾,并且实现在两端都能添加与删除结点。
=== "LinkedListDeque" === "LinkedListDeque"
@ -316,7 +320,7 @@ comments: true
=== "pollFirst()" === "pollFirst()"
![linkedlist_deque_poll_first](deque.assets/linkedlist_deque_poll_first.png) ![linkedlist_deque_poll_first](deque.assets/linkedlist_deque_poll_first.png)
以下是使用双向链表实现双向队列的示例代码。 以下是具体实现代码。
=== "Java" === "Java"
@ -385,3 +389,84 @@ comments: true
```zig title="linkedlist_deque.zig" ```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}-[func]{}
```
=== "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"
```