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>
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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++
|
||||||
}
|
}
|
||||||
|
|
151
codes/java/chapter_stack_and_queue/array_deque.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
BIN
docs/chapter_stack_and_queue/deque.assets/array_deque.png
Normal file
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 61 KiB |
|
@ -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"
|
||||||
|
|
||||||
|
```
|
||||||
|
|