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