From 33edc752c825b90f206ef673d814ffcef673740e Mon Sep 17 00:00:00 2001
From: krahets
Date: Thu, 14 Sep 2023 03:34:29 +0800
Subject: [PATCH] build
---
chapter_heap/build_heap.md | 15 +++----
chapter_heap/heap.md | 2 +-
chapter_stack_and_queue/deque.md | 64 +++++++++++++++++-------------
chapter_stack_and_queue/queue.md | 15 ++++---
chapter_stack_and_queue/stack.md | 24 +++++------
chapter_stack_and_queue/summary.md | 8 ++++
index.md | 19 ++++-----
7 files changed, 80 insertions(+), 67 deletions(-)
diff --git a/chapter_heap/build_heap.md b/chapter_heap/build_heap.md
index f688a1198..aed127200 100644
--- a/chapter_heap/build_heap.md
+++ b/chapter_heap/build_heap.md
@@ -6,25 +6,26 @@ comments: true
在某些情况下,我们希望使用一个列表的所有元素来构建一个堆,这个过程被称为“建堆操作”。
-## 8.2.1 自上而下构建
+## 8.2.1 借助入堆操作实现
我们首先创建一个空堆,然后遍历列表,依次对每个元素执行“入堆操作”,即先将元素添加至堆的尾部,再对该元素执行“从底至顶”堆化。
-每当一个元素入堆,堆的长度就加一,因此堆是“自上而下”地构建的。
+每当一个元素入堆,堆的长度就加一。由于节点是从顶到底依次被添加进二叉树的,因此堆是“自上而下”地构建的。
设元素数量为 $n$ ,每个元素的入堆操作使用 $O(\log{n})$ 时间,因此该建堆方法的时间复杂度为 $O(n \log n)$ 。
-## 8.2.2 自下而上构建
+## 8.2.2 通过遍历堆化实现
实际上,我们可以实现一种更为高效的建堆方法,共分为两步。
-1. 将列表所有元素原封不动添加到堆中。
+1. 将列表所有元素原封不动添加到堆中,此时堆的性质尚未得到满足。
2. 倒序遍历堆(即层序遍历的倒序),依次对每个非叶节点执行“从顶至底堆化”。
-在倒序遍历中,堆是“自下而上”地构建的,需要重点理解以下两点。
+**每当堆化一个节点后,以该节点为根节点的子树就形成一个合法的子堆**。而由于是倒序遍历,因此堆是“自下而上”地被构建的。
-- 由于叶节点没有子节点,因此无需对它们执行堆化。最后一个节点的父节点是最后一个非叶节点。
-- 在倒序遍历中,我们能够保证当前节点之下的子树已经完成堆化(已经是合法的堆),而这是堆化当前节点的前置条件。
+之所以选择倒序遍历,是因为这样能够保证当前节点之下的子树已经是合法的子堆,这样堆化当前节点才是有效的。
+
+值得说明的是,**叶节点没有子节点,天然就是合法的子堆,因此无需堆化**。如以下代码所示,最后一个非叶节点是最后一个节点的父节点,我们从它开始倒序遍历并执行堆化。
=== "Python"
diff --git a/chapter_heap/heap.md b/chapter_heap/heap.md
index e8ae66689..8a77a5a8e 100644
--- a/chapter_heap/heap.md
+++ b/chapter_heap/heap.md
@@ -1135,7 +1135,7 @@ comments: true
/* 元素出堆 */
void pop() {
// 判空处理
- if (empty()) {
+ if (isEmpty()) {
throw out_of_range("堆为空");
}
// 交换根节点与最右叶节点(即交换首元素与尾元素)
diff --git a/chapter_stack_and_queue/deque.md b/chapter_stack_and_queue/deque.md
index 5f1b4d35b..1ae8c0d30 100644
--- a/chapter_stack_and_queue/deque.md
+++ b/chapter_stack_and_queue/deque.md
@@ -416,9 +416,8 @@ comments: true
def pop(self, is_front: bool) -> int:
"""出队操作"""
- # 若队列为空,直接返回 None
if self.is_empty():
- return None
+ raise IndexError("双向队列为空")
# 队首出队操作
if is_front:
val: int = self.front.val # 暂存头节点值
@@ -450,11 +449,15 @@ comments: true
def peek_first(self) -> int:
"""访问队首元素"""
- return None if self.is_empty() else self.front.val
+ if self.is_empty():
+ raise IndexError("双向队列为空")
+ return self.front.val
def peek_last(self) -> int:
"""访问队尾元素"""
- return None if self.is_empty() else self.rear.val
+ if self.is_empty():
+ raise IndexError("双向队列为空")
+ return self.rear.val
def to_array(self) -> list[int]:
"""返回数组用于打印"""
@@ -544,9 +547,8 @@ comments: true
/* 出队操作 */
int pop(bool isFront) {
- // 若队列为空,直接返回 -1
if (isEmpty())
- return -1;
+ throw out_of_range("队列为空");
int val;
// 队首出队操作
if (isFront) {
@@ -587,12 +589,16 @@ comments: true
/* 访问队首元素 */
int peekFirst() {
- return isEmpty() ? -1 : front->val;
+ if (isEmpty())
+ throw out_of_range("双向队列为空");
+ return front->val;
}
/* 访问队尾元素 */
int peekLast() {
- return isEmpty() ? -1 : rear->val;
+ if (isEmpty())
+ throw out_of_range("双向队列为空");
+ return rear->val;
}
/* 返回数组用于打印 */
@@ -675,10 +681,9 @@ comments: true
}
/* 出队操作 */
- private Integer pop(boolean isFront) {
- // 若队列为空,直接返回 null
+ private int pop(boolean isFront) {
if (isEmpty())
- return null;
+ throw new IndexOutOfBoundsException();
int val;
// 队首出队操作
if (isFront) {
@@ -706,23 +711,27 @@ comments: true
}
/* 队首出队 */
- public Integer popFirst() {
+ public int popFirst() {
return pop(true);
}
/* 队尾出队 */
- public Integer popLast() {
+ public int popLast() {
return pop(false);
}
/* 访问队首元素 */
- public Integer peekFirst() {
- return isEmpty() ? null : front.val;
+ public int peekFirst() {
+ if (isEmpty())
+ throw new IndexOutOfBoundsException();
+ return front.val;
}
/* 访问队尾元素 */
- public Integer peekLast() {
- return isEmpty() ? null : rear.val;
+ public int peekLast() {
+ if (isEmpty())
+ throw new IndexOutOfBoundsException();
+ return rear.val;
}
/* 返回数组用于打印 */
@@ -812,34 +821,29 @@ comments: true
/* 出队操作 */
private int? pop(bool isFront) {
- // 若队列为空,直接返回 null
- if (isEmpty()) {
- return null;
- }
-
+ if (isEmpty())
+ throw new Exception();
int val;
// 队首出队操作
if (isFront) {
val = front.val; // 暂存头节点值
- // 删除头节点
+ // 删除头节点
ListNode fNext = front.next;
if (fNext != null) {
fNext.prev = null;
front.next = null;
}
-
front = fNext; // 更新头节点
}
// 队尾出队操作
else {
val = rear.val; // 暂存尾节点值
- // 删除尾节点
+ // 删除尾节点
ListNode rPrev = rear.prev;
if (rPrev != null) {
rPrev.next = null;
rear.prev = null;
}
-
rear = rPrev; // 更新尾节点
}
@@ -859,12 +863,16 @@ comments: true
/* 访问队首元素 */
public int? peekFirst() {
- return isEmpty() ? null : front.val;
+ if (isEmpty())
+ throw new Exception();
+ return front.val;
}
/* 访问队尾元素 */
public int? peekLast() {
- return isEmpty() ? null : rear.val;
+ if (isEmpty())
+ throw new Exception();
+ return rear.val;
}
/* 返回数组用于打印 */
diff --git a/chapter_stack_and_queue/queue.md b/chapter_stack_and_queue/queue.md
index 18487bd6a..3dd6fede5 100755
--- a/chapter_stack_and_queue/queue.md
+++ b/chapter_stack_and_queue/queue.md
@@ -358,9 +358,8 @@ comments: true
def peek(self) -> int:
"""访问队首元素"""
- if self.size() == 0:
- print("队列为空")
- return False
+ if self.is_empty():
+ raise IndexError("队列为空")
return self.__front.val
def to_list(self) -> list[int]:
@@ -400,7 +399,7 @@ comments: true
}
/* 判断队列是否为空 */
- bool empty() {
+ bool isEmpty() {
return queSize == 0;
}
@@ -502,7 +501,7 @@ comments: true
/* 访问队首元素 */
public int peek() {
- if (size() == 0)
+ if (isEmpty())
throw new IndexOutOfBoundsException();
return front.val;
}
@@ -570,7 +569,7 @@ comments: true
/* 访问队首元素 */
public int peek() {
- if (size() == 0 || front == null)
+ if (isEmpty())
throw new Exception();
return front.val;
}
@@ -1303,7 +1302,7 @@ comments: true
}
/* 判断队列是否为空 */
- bool empty() {
+ bool isEmpty() {
return size() == 0;
}
@@ -1331,7 +1330,7 @@ comments: true
/* 访问队首元素 */
int peek() {
- if (empty())
+ if (isEmpty())
throw out_of_range("队列为空");
return nums[front];
}
diff --git a/chapter_stack_and_queue/stack.md b/chapter_stack_and_queue/stack.md
index 47b1076b5..cd1c387af 100755
--- a/chapter_stack_and_queue/stack.md
+++ b/chapter_stack_and_queue/stack.md
@@ -344,16 +344,15 @@ comments: true
def pop(self) -> int:
"""出栈"""
- num: int = self.peek()
+ num = self.peek()
self.__peek = self.__peek.next
self.__size -= 1
return num
def peek(self) -> int:
"""访问栈顶元素"""
- # 判空处理
- if not self.__peek:
- return None
+ if self.is_empty():
+ raise IndexError("栈为空")
return self.__peek.val
def to_list(self) -> list[int]:
@@ -393,7 +392,7 @@ comments: true
}
/* 判断栈是否为空 */
- bool empty() {
+ bool isEmpty() {
return size() == 0;
}
@@ -417,7 +416,7 @@ comments: true
/* 访问栈顶元素 */
int top() {
- if (size() == 0)
+ if (isEmpty())
throw out_of_range("栈为空");
return stackTop->val;
}
@@ -475,7 +474,7 @@ comments: true
/* 访问栈顶元素 */
public int peek() {
- if (size() == 0)
+ if (isEmpty())
throw new IndexOutOfBoundsException();
return stackPeek.val;
}
@@ -525,9 +524,6 @@ comments: true
/* 出栈 */
public int pop() {
- if (stackPeek == null)
- throw new Exception();
-
int num = peek();
stackPeek = stackPeek.next;
stkSize--;
@@ -536,7 +532,7 @@ comments: true
/* 访问栈顶元素 */
public int peek() {
- if (size() == 0 || stackPeek == null)
+ if (isEmpty())
throw new Exception();
return stackPeek.val;
}
@@ -1139,8 +1135,8 @@ comments: true
}
/* 判断栈是否为空 */
- bool empty() {
- return stack.empty();
+ bool isEmpty() {
+ return stack.size() == 0;
}
/* 入栈 */
@@ -1156,7 +1152,7 @@ comments: true
/* 访问栈顶元素 */
int top() {
- if (empty())
+ if (isEmpty())
throw out_of_range("栈为空");
return stack.back();
}
diff --git a/chapter_stack_and_queue/summary.md b/chapter_stack_and_queue/summary.md
index fab68752b..94340bcd1 100644
--- a/chapter_stack_and_queue/summary.md
+++ b/chapter_stack_and_queue/summary.md
@@ -25,3 +25,11 @@ comments: true
!!! question "双向队列像是两个栈拼接在了一起,它的用途是什么?"
双向队列就像是栈和队列的组合,或者是两个栈拼在了一起。它表现的是栈 + 队列的逻辑,因此可以实现栈与队列的所有应用,并且更加灵活。
+
+!!! question "撤销(undo)和反撤销(redo)具体是如何实现的?"
+
+ 使用两个堆栈,栈 `A` 用于撤销,栈 `B` 用于反撤销。
+
+ 1. 每当用户执行一个操作,将这个操作压入栈 `A` ,并清空栈 `B` 。
+ 2. 当用户执行“撤销”时,从栈 `A` 中弹出最近的操作,并将其压入栈 `B` 。
+ 3. 当用户执行“反撤销”时,从栈 `B` 中弹出最近的操作,并将其压入栈 `A` 。
diff --git a/index.md b/index.md
index eb668b3e6..e3b0538be 100644
--- a/index.md
+++ b/index.md
@@ -106,19 +106,20 @@ hide:
-本书的代码审阅工作由 Gonglja, gvenusleo, justin‐tse, krahets, nuomi1, Reanon, sjinzh 完成(按照首字母顺序排列)。感谢他们付出的时间与精力,正是他们确保了各语言代码的规范与统一。
+本书的代码审阅工作由 Gonglja, gvenusleo, justin‐tse, krahets, night-cruise, nuomi1, Reanon, sjinzh 完成(按照首字母顺序排列)。感谢他们付出的时间与精力,正是他们确保了各语言代码的规范与统一。