From a59e1f805443ef2accb413e88c420ab37e4ef443 Mon Sep 17 00:00:00 2001 From: krahets Date: Thu, 16 Feb 2023 02:20:47 +0800 Subject: [PATCH] build --- chapter_stack_and_queue/deque.md | 214 ++++++++++++++++++++++++++++--- chapter_stack_and_queue/queue.md | 18 +-- 2 files changed, 208 insertions(+), 24 deletions(-) diff --git a/chapter_stack_and_queue/deque.md b/chapter_stack_and_queue/deque.md index 7a6fa0d57..bd5382637 100644 --- a/chapter_stack_and_queue/deque.md +++ b/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" @@ -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 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" + + ``` diff --git a/chapter_stack_and_queue/queue.md b/chapter_stack_and_queue/queue.md index c3cfc5fa3..53ab58695 100755 --- a/chapter_stack_and_queue/queue.md +++ b/chapter_stack_and_queue/queue.md @@ -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; }