diff --git a/codes/c/chapter_stack_and_queue/array_queue.c b/codes/c/chapter_stack_and_queue/array_queue.c index 6a67c6393..049a3dd87 100644 --- a/codes/c/chapter_stack_and_queue/array_queue.c +++ b/codes/c/chapter_stack_and_queue/array_queue.c @@ -62,7 +62,7 @@ void push(ArrayQueue *queue, int num) { // 计算队尾指针,指向队尾索引 + 1 // 通过取余操作,实现 rear 越过数组尾部后回到头部 int rear = (queue->front + queue->queSize) % queue->queCapacity; - // 尾结点后添加 num + // 将 num 添加至队尾 queue->nums[rear] = num; queue->queSize++; } diff --git a/codes/cpp/chapter_stack_and_queue/array_queue.cpp b/codes/cpp/chapter_stack_and_queue/array_queue.cpp index 696967f15..eafc8ce24 100644 --- a/codes/cpp/chapter_stack_and_queue/array_queue.cpp +++ b/codes/cpp/chapter_stack_and_queue/array_queue.cpp @@ -50,7 +50,7 @@ public: // 计算队尾指针,指向队尾索引 + 1 // 通过取余操作,实现 rear 越过数组尾部后回到头部 int rear = (front + queSize) % queCapacity; - // 尾结点后添加 num + // 将 num 添加至队尾 nums[rear] = num; queSize++; } diff --git a/codes/csharp/chapter_stack_and_queue/array_queue.cs b/codes/csharp/chapter_stack_and_queue/array_queue.cs index 51160eb64..9dce56861 100644 --- a/codes/csharp/chapter_stack_and_queue/array_queue.cs +++ b/codes/csharp/chapter_stack_and_queue/array_queue.cs @@ -50,7 +50,7 @@ class ArrayQueue // 计算尾指针,指向队尾索引 + 1 // 通过取余操作,实现 rear 越过数组尾部后回到头部 int rear = (front + queSize) % capacity(); - // 尾结点后添加 num + // 将 num 添加至队尾 nums[rear] = num; queSize++; } diff --git a/codes/go/chapter_stack_and_queue/array_queue.go b/codes/go/chapter_stack_and_queue/array_queue.go index 226fd53af..f72402767 100644 --- a/codes/go/chapter_stack_and_queue/array_queue.go +++ b/codes/go/chapter_stack_and_queue/array_queue.go @@ -41,7 +41,7 @@ func (q *arrayQueue) push(num int) { // 计算尾指针,指向队尾索引 + 1 // 通过取余操作,实现 rear 越过数组尾部后回到头部 rear := (q.front + q.queSize) % q.queCapacity - // 尾结点后添加 num + // 将 num 添加至队尾 q.nums[rear] = num q.queSize++ } diff --git a/codes/java/chapter_stack_and_queue/array_deque.java b/codes/java/chapter_stack_and_queue/array_deque.java new file mode 100644 index 000000000..cd9e798a0 --- /dev/null +++ b/codes/java/chapter_stack_and_queue/array_deque.java @@ -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); + } +} diff --git a/codes/java/chapter_stack_and_queue/array_queue.java b/codes/java/chapter_stack_and_queue/array_queue.java index 8552fd113..f3bd8b0eb 100644 --- a/codes/java/chapter_stack_and_queue/array_queue.java +++ b/codes/java/chapter_stack_and_queue/array_queue.java @@ -43,7 +43,7 @@ class ArrayQueue { // 计算尾指针,指向队尾索引 + 1 // 通过取余操作,实现 rear 越过数组尾部后回到头部 int rear = (front + queSize) % capacity(); - // 尾结点后添加 num + // 将 num 添加至队尾 nums[rear] = num; queSize++; } diff --git a/codes/java/chapter_stack_and_queue/linkedlist_deque.java b/codes/java/chapter_stack_and_queue/linkedlist_deque.java index 68c8a4a81..9b748fbdd 100644 --- a/codes/java/chapter_stack_and_queue/linkedlist_deque.java +++ b/codes/java/chapter_stack_and_queue/linkedlist_deque.java @@ -121,19 +121,15 @@ class LinkedListDeque { 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 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; } } @@ -144,8 +140,7 @@ public class linkedlist_deque { deque.pushLast(3); deque.pushLast(2); deque.pushLast(5); - System.out.print("双向队列 deque = "); - deque.print(); + System.out.println("双向队列 deque = " + Arrays.toString(deque.toArray())); /* 访问元素 */ int peekFirst = deque.peekFirst(); @@ -155,19 +150,15 @@ public class linkedlist_deque { /* 元素入队 */ deque.pushLast(4); - System.out.print("元素 4 队尾入队后 deque = "); - deque.print(); + System.out.println("元素 4 队尾入队后 deque = " + Arrays.toString(deque.toArray())); deque.pushFirst(1); - System.out.print("元素 1 队首入队后 deque = "); - deque.print(); + System.out.println("元素 1 队首入队后 deque = " + Arrays.toString(deque.toArray())); /* 元素出队 */ int pollLast = deque.pollLast(); - System.out.print("队尾出队元素 = " + pollLast + ",队尾出队后 deque = "); - deque.print(); + System.out.println("队尾出队元素 = " + pollLast + ",队尾出队后 deque = " + Arrays.toString(deque.toArray())); int pollFirst = deque.pollFirst(); - System.out.print("队首出队元素 = " + pollFirst + ",队首出队后 deque = "); - deque.print(); + System.out.println("队首出队元素 = " + pollFirst + ",队首出队后 deque = " + Arrays.toString(deque.toArray())); /* 获取双向队列的长度 */ int size = deque.size(); diff --git a/codes/javascript/chapter_stack_and_queue/array_queue.js b/codes/javascript/chapter_stack_and_queue/array_queue.js index c8d297186..35584f282 100644 --- a/codes/javascript/chapter_stack_and_queue/array_queue.js +++ b/codes/javascript/chapter_stack_and_queue/array_queue.js @@ -38,7 +38,7 @@ class ArrayQueue { // 计算尾指针,指向队尾索引 + 1 // 通过取余操作,实现 rear 越过数组尾部后回到头部 const rear = (this.#front + this.size) % this.capacity; - // 尾结点后添加 num + // 将 num 添加至队尾 this.#nums[rear] = num; this.#queSize++; } diff --git a/codes/python/chapter_stack_and_queue/array_queue.py b/codes/python/chapter_stack_and_queue/array_queue.py index 2420c67aa..e14e2ec61 100644 --- a/codes/python/chapter_stack_and_queue/array_queue.py +++ b/codes/python/chapter_stack_and_queue/array_queue.py @@ -35,7 +35,7 @@ class ArrayQueue: # 计算尾指针,指向队尾索引 + 1 # 通过取余操作,实现 rear 越过数组尾部后回到头部 rear = (self.__front + self.__size) % self.capacity() - # 尾结点后添加 num + # 将 num 添加至队尾 self.__nums[rear] = num self.__size += 1 diff --git a/codes/swift/chapter_stack_and_queue/array_queue.swift b/codes/swift/chapter_stack_and_queue/array_queue.swift index c04da4362..0d39687a3 100644 --- a/codes/swift/chapter_stack_and_queue/array_queue.swift +++ b/codes/swift/chapter_stack_and_queue/array_queue.swift @@ -39,7 +39,7 @@ class ArrayQueue { // 计算尾指针,指向队尾索引 + 1 // 通过取余操作,实现 rear 越过数组尾部后回到头部 let rear = (front + queSize) % capacity() - // 尾结点后添加 num + // 将 num 添加至队尾 nums[rear] = num queSize += 1 } diff --git a/codes/typescript/chapter_stack_and_queue/array_queue.ts b/codes/typescript/chapter_stack_and_queue/array_queue.ts index a445a97a6..565fdf2cf 100644 --- a/codes/typescript/chapter_stack_and_queue/array_queue.ts +++ b/codes/typescript/chapter_stack_and_queue/array_queue.ts @@ -39,7 +39,7 @@ class ArrayQueue { // 计算尾指针,指向队尾索引 + 1 // 通过取余操作,实现 rear 越过数组尾部后回到头部 const rear = (this.front + this.queSize) % this.capacity; - // 尾结点后添加 num + // 将 num 添加至队尾 this.nums[rear] = num; this.queSize++; } diff --git a/codes/zig/chapter_stack_and_queue/array_queue.zig b/codes/zig/chapter_stack_and_queue/array_queue.zig index 5b92bec67..1ac5131b1 100644 --- a/codes/zig/chapter_stack_and_queue/array_queue.zig +++ b/codes/zig/chapter_stack_and_queue/array_queue.zig @@ -58,7 +58,7 @@ pub fn ArrayQueue(comptime T: type) type { // 计算尾指针,指向队尾索引 + 1 // 通过取余操作,实现 rear 越过数组尾部后回到头部 var rear = (self.front + self.queSize) % self.capacity(); - // 尾结点后添加 num + // 将 num 添加至队尾 self.nums[rear] = num; self.queSize += 1; } diff --git a/docs/chapter_stack_and_queue/deque.assets/array_deque.png b/docs/chapter_stack_and_queue/deque.assets/array_deque.png new file mode 100644 index 000000000..ac9248727 Binary files /dev/null and b/docs/chapter_stack_and_queue/deque.assets/array_deque.png differ diff --git a/docs/chapter_stack_and_queue/deque.assets/array_deque_poll_first.png b/docs/chapter_stack_and_queue/deque.assets/array_deque_poll_first.png new file mode 100644 index 000000000..5dab67eff Binary files /dev/null and b/docs/chapter_stack_and_queue/deque.assets/array_deque_poll_first.png differ diff --git a/docs/chapter_stack_and_queue/deque.assets/array_deque_poll_last.png b/docs/chapter_stack_and_queue/deque.assets/array_deque_poll_last.png new file mode 100644 index 000000000..4e4b0a456 Binary files /dev/null and b/docs/chapter_stack_and_queue/deque.assets/array_deque_poll_last.png differ diff --git a/docs/chapter_stack_and_queue/deque.assets/array_deque_push_first.png b/docs/chapter_stack_and_queue/deque.assets/array_deque_push_first.png new file mode 100644 index 000000000..fe68fbdf8 Binary files /dev/null and b/docs/chapter_stack_and_queue/deque.assets/array_deque_push_first.png differ diff --git a/docs/chapter_stack_and_queue/deque.assets/array_deque_push_last.png b/docs/chapter_stack_and_queue/deque.assets/array_deque_push_last.png new file mode 100644 index 000000000..ee429455e Binary files /dev/null and b/docs/chapter_stack_and_queue/deque.assets/array_deque_push_last.png differ diff --git a/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque.png b/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque.png index 2337972f7..e0e64bde2 100644 Binary files a/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque.png and b/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque.png differ diff --git a/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_poll_first.png b/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_poll_first.png index db3d9a3ce..c9d0700fd 100644 Binary files a/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_poll_first.png and b/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_poll_first.png differ diff --git a/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_poll_last.png b/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_poll_last.png index c881168b5..40a773374 100644 Binary files a/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_poll_last.png and b/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_poll_last.png differ diff --git a/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_push_first.png b/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_push_first.png index d1cfaeb9b..413710340 100644 Binary files a/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_push_first.png and b/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_push_first.png differ diff --git a/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_push_last.png b/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_push_last.png index 408990870..c4b1296a5 100644 Binary files a/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_push_last.png and b/docs/chapter_stack_and_queue/deque.assets/linkedlist_deque_push_last.png differ diff --git a/docs/chapter_stack_and_queue/deque.md b/docs/chapter_stack_and_queue/deque.md index 81cd34ce6..7e85c6ce5 100644 --- a/docs/chapter_stack_and_queue/deque.md +++ b/docs/chapter_stack_and_queue/deque.md @@ -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" @@ -385,3 +389,84 @@ 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}-[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" + + ```