From 190c4275cdff01cd2e28b18fc407cdc3b7dc41f2 Mon Sep 17 00:00:00 2001 From: krahets Date: Fri, 26 May 2023 20:37:48 +0800 Subject: [PATCH] build --- chapter_data_structure/number_encoding.md | 6 +- chapter_sorting/heap_sort.md | 13 +- chapter_tree/binary_search_tree.md | 142 +++++++++++++++------- 3 files changed, 109 insertions(+), 52 deletions(-) diff --git a/chapter_data_structure/number_encoding.md b/chapter_data_structure/number_encoding.md index 40e0070da..cc2ea2396 100644 --- a/chapter_data_structure/number_encoding.md +++ b/chapter_data_structure/number_encoding.md @@ -49,8 +49,10 @@ $$ 另一方面,**数字零的原码有 $+0$ 和 $-0$ 两种表示方式**。这意味着数字零对应着两个不同的二进制编码,而这可能会带来歧义问题。例如,在条件判断中,如果没有区分正零和负零,可能会导致错误的判断结果。如果我们想要处理正零和负零歧义,则需要引入额外的判断操作,其可能会降低计算机的运算效率。 $$ -+0 = 0000 \space 0000 \newline --0 = 1000 \space 0000 +\begin{aligned} ++0 & = 0000 \space 0000 \newline +-0 & = 1000 \space 0000 +\end{aligned} $$ 与原码一样,反码也存在正负零歧义问题。为此,计算机进一步引入了「补码」。那么,补码有什么作用呢?我们先来分析一下负零的补码的计算过程: diff --git a/chapter_sorting/heap_sort.md b/chapter_sorting/heap_sort.md index ef0ba885e..e67bd4361 100644 --- a/chapter_sorting/heap_sort.md +++ b/chapter_sorting/heap_sort.md @@ -11,10 +11,13 @@ comments: true 「堆排序 Heap Sort」是一种基于堆数据结构实现的高效排序算法。我们可以利用已经学过的“建堆操作”和“元素出堆操作”实现堆排序: 1. 输入数组并建立小顶堆,此时最小元素位于堆顶。 -2. 初始化一个数组 `res` ,用于存储排序结果。 -3. 循环执行 $n$ 轮出堆操作,并依次将出堆元素记录至 `res` ,即可得到从小到大排序的序列。 +2. 不断执行出堆操作,依次记录出堆元素,即可得到从小到大排序的序列。 -该方法虽然可行,但需要借助一个额外数组,比较浪费空间。在实际中,我们通常使用一种更加优雅的实现方式。设数组的长度为 $n$ ,堆排序的流程如下: +以上方法虽然可行,但需要借助一个额外数组来保存弹出的元素,比较浪费空间。在实际中,我们通常使用一种更加优雅的实现方式。 + +## 11.7.1.   算法流程 + +设数组的长度为 $n$ ,堆排序的流程如下: 1. 输入数组并建立大顶堆。完成后,最大元素位于堆顶。 2. 将堆顶元素(第一个元素)与堆底元素(最后一个元素)交换。完成交换后,堆的长度减 $1$ ,已排序元素数量加 $1$ 。 @@ -237,8 +240,8 @@ comments: true [class]{}-[func]{heapSort} ``` -## 11.7.1.   算法特性 +## 11.7.2.   算法特性 -- **时间复杂度 $O(n \log n)$ 、非自适应排序** :从堆中提取最大元素的时间复杂度为 $O(\log n)$ ,共循环 $n - 1$ 轮。 +- **时间复杂度 $O(n \log n)$ 、非自适应排序** :建堆操作使用 $O(n)$ 时间。从堆中提取最大元素的时间复杂度为 $O(\log n)$ ,共循环 $n - 1$ 轮。 - **空间复杂度 $O(1)$ 、原地排序** :几个指针变量使用 $O(1)$ 空间。元素交换和堆化操作都是在原数组上进行的。 - **非稳定排序**:在交换堆顶元素和堆底元素时,相等元素的相对位置可能发生变化。 diff --git a/chapter_tree/binary_search_tree.md b/chapter_tree/binary_search_tree.md index 7c2959f36..c8161ecb7 100755 --- a/chapter_tree/binary_search_tree.md +++ b/chapter_tree/binary_search_tree.md @@ -88,7 +88,7 @@ comments: true ```python title="binary_search_tree.py" def search(self, num: int) -> TreeNode | None: """查找节点""" - cur: TreeNode | None = self.__root + cur: TreeNode | None = self.root # 循环查找,越过叶节点后跳出 while cur is not None: # 目标节点在 cur 的右子树中 @@ -201,11 +201,14 @@ comments: true // 循环查找,越过叶节点后跳出 while (cur != null) { // 目标节点在 cur 的右子树中 - if (cur.val < num) cur = cur.right; + if (cur.val < num) cur = + cur.right; // 目标节点在 cur 的左子树中 - else if (cur.val > num) cur = cur.left; + else if (cur.val > num) + cur = cur.left; // 找到目标节点,跳出循环 - else break; + else + break; } // 返回目标节点 return cur; @@ -343,11 +346,11 @@ comments: true def insert(self, num: int) -> None: """插入节点""" # 若树为空,直接提前返回 - if self.__root is None: + if self.root is None: return # 循环查找,越过叶节点后跳出 - cur, pre = self.__root, None + cur, pre = self.root, None while cur is not None: # 找到重复节点,直接返回 if cur.val == num: @@ -501,24 +504,30 @@ comments: true /* 插入节点 */ void insert(int num) { // 若树为空,直接提前返回 - if (root == null) return; + if (root == null) + return; TreeNode? cur = root, pre = null; // 循环查找,越过叶节点后跳出 while (cur != null) { // 找到重复节点,直接返回 - if (cur.val == num) return; + if (cur.val == num) + return; pre = cur; // 插入位置在 cur 的右子树中 - if (cur.val < num) cur = cur.right; + if (cur.val < num) + cur = cur.right; // 插入位置在 cur 的左子树中 - else cur = cur.left; + else + cur = cur.left; } // 插入节点 TreeNode node = new TreeNode(num); if (pre != null) { - if (pre.val < num) pre.right = node; - else pre.left = node; + if (pre.val < num) + pre.right = node; + else + pre.left = node; } } ``` @@ -663,10 +672,15 @@ comments: true // 当子节点数量 = 0 / 1 时, child = null / 该子节点 TreeNode child = cur.left != null ? cur.left : cur.right; // 删除节点 cur - if (pre.left == cur) - pre.left = child; - else - pre.right = child; + if (cur != root) { + if (pre.left == cur) + pre.left = child; + else + pre.right = child; + } else { + // 若删除节点为根节点,则重新指定根节点 + root = child; + } } // 子节点数量 = 2 else { @@ -713,10 +727,15 @@ comments: true // 当子节点数量 = 0 / 1 时, child = nullptr / 该子节点 TreeNode *child = cur->left != nullptr ? cur->left : cur->right; // 删除节点 cur - if (pre->left == cur) - pre->left = child; - else - pre->right = child; + if (cur != root) { + if (pre->left == cur) + pre->left = child; + else + pre->right = child; + } else { + // 若删除节点为根节点,则重新指定根节点 + root = child; + } // 释放内存 delete cur; } @@ -742,11 +761,11 @@ comments: true def remove(self, num: int) -> None: """删除节点""" # 若树为空,直接提前返回 - if self.__root is None: + if self.root is None: return # 循环查找,越过叶节点后跳出 - cur, pre = self.__root, None + cur, pre = self.root, None while cur is not None: # 找到待删除节点,跳出循环 if cur.val == num: @@ -767,10 +786,14 @@ comments: true # 当子节点数量 = 0 / 1 时, child = null / 该子节点 child = cur.left or cur.right # 删除节点 cur - if pre.left == cur: - pre.left = child + if cur != self.root: + if pre.left == cur: + pre.left = child + else: + pre.right = child else: - pre.right = child + # 若删除节点为根节点,则重新指定根节点 + self.__root = cur # 子节点数量 = 2 else: # 获取中序遍历中 cur 的下一个节点 @@ -822,11 +845,16 @@ comments: true } else { child = cur.Right } - // 将子节点替换为待删除节点 - if pre.Left == cur { - pre.Left = child + // 删除节点 cur + if cur != bst.root { + if pre.Left == cur { + pre.Left = child + } else { + pre.Right = child + } } else { - pre.Right = child + // 若删除节点为根节点,则重新指定根节点 + bst.root = child } // 子节点数为 2 } else { @@ -869,8 +897,13 @@ comments: true // 当子节点数量 = 0 / 1 时, child = null / 该子节点 let child = cur.left !== null ? cur.left : cur.right; // 删除节点 cur - if (pre.left === cur) pre.left = child; - else pre.right = child; + if (cur != root) { + if (pre.left === cur) pre.left = child; + else pre.right = child; + } else { + // 若删除节点为根节点,则重新指定根节点 + root = child; + } } // 子节点数量 = 2 else { @@ -920,10 +953,15 @@ comments: true // 当子节点数量 = 0 / 1 时, child = null / 该子节点 let child = cur.left !== null ? cur.left : cur.right; // 删除节点 cur - if (pre!.left === cur) { - pre!.left = child; + if (cur != root) { + if (pre!.left === cur) { + pre!.left = child; + } else { + pre!.right = child; + } } else { - pre!.right = child; + // 若删除节点为根节点,则重新指定根节点 + root = child; } } // 子节点数量 = 2 @@ -1001,29 +1039,38 @@ comments: true /* 删除节点 */ void remove(int num) { // 若树为空,直接提前返回 - if (root == null) return; + if (root == null) + return; TreeNode? cur = root, pre = null; // 循环查找,越过叶节点后跳出 while (cur != null) { // 找到待删除节点,跳出循环 - if (cur.val == num) break; + if (cur.val == num) + break; pre = cur; // 待删除节点在 cur 的右子树中 - if (cur.val < num) cur = cur.right; + if (cur.val < num) + cur = cur.right; // 待删除节点在 cur 的左子树中 - else cur = cur.left; + else + cur = cur.left; } // 若无待删除节点,则直接返回 - if (cur == null || pre == null) return; + if (cur == null || pre == null) + return; // 子节点数量 = 0 or 1 if (cur.left == null || cur.right == null) { // 当子节点数量 = 0 / 1 时, child = null / 该子节点 TreeNode? child = cur.left != null ? cur.left : cur.right; // 删除节点 cur - if (pre.left == cur) { - pre.left = child; + if (cur != root) { + if (pre.left == cur) + pre.left = child; + else + pre.right = child; } else { - pre.right = child; + // 若删除节点为根节点,则重新指定根节点 + root = child; } } // 子节点数量 = 2 @@ -1078,10 +1125,15 @@ comments: true // 当子节点数量 = 0 / 1 时, child = null / 该子节点 let child = cur?.left != nil ? cur?.left : cur?.right // 删除节点 cur - if pre?.left === cur { - pre?.left = child + if cur != root { + if pre?.left === cur { + pre?.left = child + } else { + pre?.right = child + } } else { - pre?.right = child + // 若删除节点为根节点,则重新指定根节点 + root = cur; } } // 子节点数量 = 2