This commit is contained in:
krahets 2023-05-26 20:37:48 +08:00
parent 5842243d5a
commit 190c4275cd
3 changed files with 109 additions and 52 deletions

View File

@ -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}
$$
与原码一样,反码也存在正负零歧义问题。为此,计算机进一步引入了「补码」。那么,补码有什么作用呢?我们先来分析一下负零的补码的计算过程:

View File

@ -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)$ 空间。元素交换和堆化操作都是在原数组上进行的。
- **非稳定排序**:在交换堆顶元素和堆底元素时,相等元素的相对位置可能发生变化。

View File

@ -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