build
This commit is contained in:
parent
5f4a7728b2
commit
00adffaca7
@ -120,12 +120,7 @@ comments: true
|
||||
|
||||
### 2. 访问元素
|
||||
|
||||
数组元素被存储在连续的内存空间中,这意味着计算数组元素的内存地址非常容易。给定数组内存地址(即首元素内存地址)和某个元素的索引,我们可以使用以下公式计算得到该元素的内存地址,从而直接访问此元素。
|
||||
|
||||
```shell
|
||||
# 元素内存地址 = 数组内存地址(首元素内存地址) + 元素长度 * 元素索引
|
||||
elementAddr = firtstElementAddr + elementLength * elementIndex
|
||||
```
|
||||
数组元素被存储在连续的内存空间中,这意味着计算数组元素的内存地址非常容易。给定数组内存地址(即首元素内存地址)和某个元素的索引,我们可以使用图 4-2 所示的公式计算得到该元素的内存地址,从而直接访问此元素。
|
||||
|
||||

|
||||
|
||||
|
@ -25,11 +25,10 @@ comments: true
|
||||
|
||||
链表由结点组成,结点之间通过引用(指针)连接,各个结点可以存储不同类型的数据,例如 int、double、string、object 等。
|
||||
|
||||
相对地,数组元素则必须是相同类型的,这样才能通过计算偏移量来获取对应元素位置。例如,如果数组同时包含 int 和 long 两种类型,单个元素分别占用 4 bytes 和 8 bytes ,那么此时就不能用以下公式计算偏移量了,因为数组中包含了两种 `elementLength` 。
|
||||
相对地,数组元素则必须是相同类型的,这样才能通过计算偏移量来获取对应元素位置。例如,如果数组同时包含 int 和 long 两种类型,单个元素分别占用 4 bytes 和 8 bytes ,那么此时就不能用以下公式计算偏移量了,因为数组中包含了两种长度的元素。
|
||||
|
||||
```
|
||||
// 元素内存地址 = 数组内存地址 + 元素长度 * 元素索引
|
||||
elementAddr = firtstElementAddr + elementLength * elementIndex
|
||||
```shell
|
||||
# 元素内存地址 = 数组内存地址 + 元素长度 * 元素索引
|
||||
```
|
||||
|
||||
!!! question "删除节点后,是否需要把 `P.next` 设为 $\text{None}$ 呢?"
|
||||
|
@ -541,7 +541,7 @@ $$
|
||||
/* 带约束爬楼梯:动态规划 */
|
||||
int climbingStairsConstraintDP(int n) {
|
||||
if (n == 1 || n == 2) {
|
||||
return n;
|
||||
return 1;
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
int[][] dp = new int[n + 1][3];
|
||||
@ -565,7 +565,7 @@ $$
|
||||
/* 带约束爬楼梯:动态规划 */
|
||||
int climbingStairsConstraintDP(int n) {
|
||||
if (n == 1 || n == 2) {
|
||||
return n;
|
||||
return 1;
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
vector<vector<int>> dp(n + 1, vector<int>(3, 0));
|
||||
@ -589,7 +589,7 @@ $$
|
||||
def climbing_stairs_constraint_dp(n: int) -> int:
|
||||
"""带约束爬楼梯:动态规划"""
|
||||
if n == 1 or n == 2:
|
||||
return n
|
||||
return 1
|
||||
# 初始化 dp 表,用于存储子问题的解
|
||||
dp = [[0] * 3 for _ in range(n + 1)]
|
||||
# 初始状态:预设最小子问题的解
|
||||
@ -608,7 +608,7 @@ $$
|
||||
/* 带约束爬楼梯:动态规划 */
|
||||
func climbingStairsConstraintDP(n int) int {
|
||||
if n == 1 || n == 2 {
|
||||
return n
|
||||
return 1
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
dp := make([][3]int, n+1)
|
||||
@ -632,7 +632,7 @@ $$
|
||||
/* 带约束爬楼梯:动态规划 */
|
||||
function climbingStairsConstraintDP(n) {
|
||||
if (n === 1 || n === 2) {
|
||||
return n;
|
||||
return 1;
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
const dp = Array.from(new Array(n + 1), () => new Array(3));
|
||||
@ -656,7 +656,7 @@ $$
|
||||
/* 带约束爬楼梯:动态规划 */
|
||||
function climbingStairsConstraintDP(n: number): number {
|
||||
if (n === 1 || n === 2) {
|
||||
return n;
|
||||
return 1;
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
const dp = Array.from(
|
||||
@ -689,7 +689,7 @@ $$
|
||||
/* 带约束爬楼梯:动态规划 */
|
||||
int climbingStairsConstraintDP(int n) {
|
||||
if (n == 1 || n == 2) {
|
||||
return n;
|
||||
return 1;
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
int[,] dp = new int[n + 1, 3];
|
||||
@ -713,7 +713,7 @@ $$
|
||||
/* 带约束爬楼梯:动态规划 */
|
||||
func climbingStairsConstraintDP(n: Int) -> Int {
|
||||
if n == 1 || n == 2 {
|
||||
return n
|
||||
return 1
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
var dp = Array(repeating: Array(repeating: 0, count: 3), count: n + 1)
|
||||
@ -737,7 +737,7 @@ $$
|
||||
// 带约束爬楼梯:动态规划
|
||||
fn climbingStairsConstraintDP(comptime n: usize) i32 {
|
||||
if (n == 1 or n == 2) {
|
||||
return @intCast(n);
|
||||
return 1;
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
var dp = [_][3]i32{ [_]i32{ -1, -1, -1 } } ** (n + 1);
|
||||
@ -761,7 +761,7 @@ $$
|
||||
/* 带约束爬楼梯:动态规划 */
|
||||
int climbingStairsConstraintDP(int n) {
|
||||
if (n == 1 || n == 2) {
|
||||
return n;
|
||||
return 1;
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
List<List<int>> dp = List.generate(n + 1, (index) => List.filled(3, 0));
|
||||
@ -784,7 +784,7 @@ $$
|
||||
```rust title="climbing_stairs_constraint_dp.rs"
|
||||
/* 带约束爬楼梯:动态规划 */
|
||||
fn climbing_stairs_constraint_dp(n: usize) -> i32 {
|
||||
if n == 1 || n == 2 { return n as i32 };
|
||||
if n == 1 || n == 2 { return 1 };
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
let mut dp = vec![vec![-1; 3]; n + 1];
|
||||
// 初始状态:预设最小子问题的解
|
||||
|
@ -526,12 +526,14 @@ $$
|
||||
|
||||
在实际中,我们通常会用一些标准哈希算法,例如 MD5、SHA-1、SHA-2、SHA3 等。它们可以将任意长度的输入数据映射到恒定长度的哈希值。
|
||||
|
||||
近一个世纪以来,哈希算法处在不断升级与优化的过程中。一部分研究人员努力提升哈希算法的性能,另一部分研究人员和黑客则致力于寻找哈希算法的安全性问题。
|
||||
近一个世纪以来,哈希算法处在不断升级与优化的过程中。一部分研究人员努力提升哈希算法的性能,另一部分研究人员和黑客则致力于寻找哈希算法的安全性问题。表 6-2 展示了在实际应用中常见的哈希算法。
|
||||
|
||||
- MD5 和 SHA-1 已多次被成功攻击,因此它们被各类安全应用弃用。
|
||||
- SHA-2 系列中的 SHA-256 是最安全的哈希算法之一,仍未出现成功的攻击案例,因此常被用在各类安全应用与协议中。
|
||||
- SHA-3 相较 SHA-2 的实现开销更低、计算效率更高,但目前使用覆盖度不如 SHA-2 系列。
|
||||
|
||||
<p align="center"> 表 6-2 常见的哈希算法 </p>
|
||||
|
||||
<div class="center-table" markdown>
|
||||
|
||||
| | MD5 | SHA-1 | SHA-2 | SHA-3 |
|
||||
|
@ -1186,12 +1186,12 @@ comments: true
|
||||
|
||||
由于数组删除首元素的时间复杂度为 $O(n)$ ,这会导致出队操作效率较低。然而,我们可以采用以下巧妙方法来避免这个问题。
|
||||
|
||||
我们可以使用一个变量 `front` 指向队首元素的索引,并维护一个变量 `queSize` 用于记录队列长度。定义 `rear = front + queSize` ,这个公式计算出的 `rear` 指向队尾元素之后的下一个位置。
|
||||
我们可以使用一个变量 `front` 指向队首元素的索引,并维护一个变量 `size` 用于记录队列长度。定义 `rear = front + size` ,这个公式计算出的 `rear` 指向队尾元素之后的下一个位置。
|
||||
|
||||
基于此设计,**数组中包含元素的有效区间为 `[front, rear - 1]`**,各种操作的实现方法如图 5-6 所示。
|
||||
|
||||
- 入队操作:将输入元素赋值给 `rear` 索引处,并将 `queSize` 增加 1 。
|
||||
- 出队操作:只需将 `front` 增加 1 ,并将 `queSize` 减少 1 。
|
||||
- 入队操作:将输入元素赋值给 `rear` 索引处,并将 `size` 增加 1 。
|
||||
- 出队操作:只需将 `front` 增加 1 ,并将 `size` 减少 1 。
|
||||
|
||||
可以看到,入队和出队操作都只需进行一次操作,时间复杂度均为 $O(1)$ 。
|
||||
|
||||
|
@ -92,7 +92,7 @@ comments: true
|
||||
```python title="binary_search_tree.py"
|
||||
def search(self, num: int) -> TreeNode | None:
|
||||
"""查找节点"""
|
||||
cur: TreeNode | None = self.root
|
||||
cur = self.__root
|
||||
# 循环查找,越过叶节点后跳出
|
||||
while cur is not None:
|
||||
# 目标节点在 cur 的右子树中
|
||||
@ -135,8 +135,8 @@ comments: true
|
||||
|
||||
```javascript title="binary_search_tree.js"
|
||||
/* 查找节点 */
|
||||
function search(num) {
|
||||
let cur = root;
|
||||
search(num) {
|
||||
let cur = this.root;
|
||||
// 循环查找,越过叶节点后跳出
|
||||
while (cur !== null) {
|
||||
// 目标节点在 cur 的右子树中
|
||||
@ -340,9 +340,11 @@ comments: true
|
||||
```java title="binary_search_tree.java"
|
||||
/* 插入节点 */
|
||||
void insert(int num) {
|
||||
// 若树为空,直接提前返回
|
||||
if (root == null)
|
||||
// 若树为空,则初始化根节点
|
||||
if (root == null) {
|
||||
root = new TreeNode(num);
|
||||
return;
|
||||
}
|
||||
TreeNode cur = root, pre = null;
|
||||
// 循环查找,越过叶节点后跳出
|
||||
while (cur != null) {
|
||||
@ -371,9 +373,11 @@ comments: true
|
||||
```cpp title="binary_search_tree.cpp"
|
||||
/* 插入节点 */
|
||||
void insert(int num) {
|
||||
// 若树为空,直接提前返回
|
||||
if (root == nullptr)
|
||||
// 若树为空,则初始化根节点
|
||||
if (root == nullptr) {
|
||||
root = new TreeNode(num);
|
||||
return;
|
||||
}
|
||||
TreeNode *cur = root, *pre = nullptr;
|
||||
// 循环查找,越过叶节点后跳出
|
||||
while (cur != nullptr) {
|
||||
@ -402,12 +406,12 @@ comments: true
|
||||
```python title="binary_search_tree.py"
|
||||
def insert(self, num: int):
|
||||
"""插入节点"""
|
||||
# 若树为空,直接提前返回
|
||||
if self.root is None:
|
||||
# 若树为空,则初始化根节点
|
||||
if self.__root is None:
|
||||
self.__root = TreeNode(num)
|
||||
return
|
||||
|
||||
# 循环查找,越过叶节点后跳出
|
||||
cur, pre = self.root, None
|
||||
cur, pre = self.__root, None
|
||||
while cur is not None:
|
||||
# 找到重复节点,直接返回
|
||||
if cur.val == num:
|
||||
@ -419,7 +423,6 @@ comments: true
|
||||
# 插入位置在 cur 的左子树中
|
||||
else:
|
||||
cur = cur.left
|
||||
|
||||
# 插入节点
|
||||
node = TreeNode(num)
|
||||
if pre.val < num:
|
||||
@ -434,8 +437,9 @@ comments: true
|
||||
/* 插入节点 */
|
||||
func (bst *binarySearchTree) insert(num int) {
|
||||
cur := bst.root
|
||||
// 若树为空,直接提前返回
|
||||
// 若树为空,则初始化根节点
|
||||
if cur == nil {
|
||||
bst.root = NewTreeNode(num)
|
||||
return
|
||||
}
|
||||
// 待插入节点之前的节点位置
|
||||
@ -466,10 +470,13 @@ comments: true
|
||||
|
||||
```javascript title="binary_search_tree.js"
|
||||
/* 插入节点 */
|
||||
function insert(num) {
|
||||
// 若树为空,直接提前返回
|
||||
if (root === null) return;
|
||||
let cur = root,
|
||||
insert(num) {
|
||||
// 若树为空,则初始化根节点
|
||||
if (this.root === null) {
|
||||
this.root = new TreeNode(num);
|
||||
return;
|
||||
}
|
||||
let cur = this.root,
|
||||
pre = null;
|
||||
// 循环查找,越过叶节点后跳出
|
||||
while (cur !== null) {
|
||||
@ -493,8 +500,9 @@ comments: true
|
||||
```typescript title="binary_search_tree.ts"
|
||||
/* 插入节点 */
|
||||
function insert(num: number): void {
|
||||
// 若树为空,直接提前返回
|
||||
// 若树为空,则初始化根节点
|
||||
if (root === null) {
|
||||
root = new TreeNode(num);
|
||||
return;
|
||||
}
|
||||
let cur = root,
|
||||
@ -526,9 +534,11 @@ comments: true
|
||||
```c title="binary_search_tree.c"
|
||||
/* 插入节点 */
|
||||
void insert(binarySearchTree *bst, int num) {
|
||||
// 若树为空,直接提前返回
|
||||
if (bst->root == NULL)
|
||||
// 若树为空,则初始化根节点
|
||||
if (bst->root == NULL) {
|
||||
bst->root = newTreeNode(num);
|
||||
return;
|
||||
}
|
||||
TreeNode *cur = bst->root, *pre = NULL;
|
||||
// 循环查找,越过叶节点后跳出
|
||||
while (cur != NULL) {
|
||||
@ -560,9 +570,11 @@ comments: true
|
||||
```csharp title="binary_search_tree.cs"
|
||||
/* 插入节点 */
|
||||
void insert(int num) {
|
||||
// 若树为空,直接提前返回
|
||||
if (root == null)
|
||||
// 若树为空,则初始化根节点
|
||||
if (root == null) {
|
||||
root = new TreeNode(num);
|
||||
return;
|
||||
}
|
||||
TreeNode? cur = root, pre = null;
|
||||
// 循环查找,越过叶节点后跳出
|
||||
while (cur != null) {
|
||||
@ -594,8 +606,9 @@ comments: true
|
||||
```swift title="binary_search_tree.swift"
|
||||
/* 插入节点 */
|
||||
func insert(num: Int) {
|
||||
// 若树为空,直接提前返回
|
||||
// 若树为空,则初始化根节点
|
||||
if root == nil {
|
||||
root = TreeNode(x: num)
|
||||
return
|
||||
}
|
||||
var cur = root
|
||||
@ -631,8 +644,11 @@ comments: true
|
||||
```zig title="binary_search_tree.zig"
|
||||
// 插入节点
|
||||
fn insert(self: *Self, num: T) !void {
|
||||
// 若树为空,直接提前返回
|
||||
if (self.root == null) return;
|
||||
// 若树为空,则初始化根节点
|
||||
if (self.root == null) {
|
||||
self.root = try self.mem_allocator.create(inc.TreeNode(T));
|
||||
return;
|
||||
}
|
||||
var cur = self.root;
|
||||
var pre: ?*inc.TreeNode(T) = null;
|
||||
// 循环查找,越过叶节点后跳出
|
||||
@ -664,8 +680,11 @@ comments: true
|
||||
```dart title="binary_search_tree.dart"
|
||||
/* 插入节点 */
|
||||
void insert(int num) {
|
||||
// 若树为空,直接提前返回
|
||||
if (_root == null) return;
|
||||
// 若树为空,则初始化根节点
|
||||
if (_root == null) {
|
||||
_root = TreeNode(num);
|
||||
return;
|
||||
}
|
||||
TreeNode? cur = _root;
|
||||
TreeNode? pre = null;
|
||||
// 循环查找,越过叶节点后跳出
|
||||
@ -694,8 +713,9 @@ comments: true
|
||||
```rust title="binary_search_tree.rs"
|
||||
/* 插入节点 */
|
||||
pub fn insert(&mut self, num: i32) {
|
||||
// 若树为空,直接提前返回
|
||||
// 若树为空,则初始化根节点
|
||||
if self.root.is_none() {
|
||||
self.root = TreeNode::new(num);
|
||||
return;
|
||||
}
|
||||
let mut cur = self.root.clone();
|
||||
@ -891,11 +911,10 @@ comments: true
|
||||
def remove(self, num: int):
|
||||
"""删除节点"""
|
||||
# 若树为空,直接提前返回
|
||||
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:
|
||||
@ -916,14 +935,14 @@ comments: true
|
||||
# 当子节点数量 = 0 / 1 时, child = null / 该子节点
|
||||
child = cur.left or cur.right
|
||||
# 删除节点 cur
|
||||
if cur != self.root:
|
||||
if cur != self.__root:
|
||||
if pre.left == cur:
|
||||
pre.left = child
|
||||
else:
|
||||
pre.right = child
|
||||
else:
|
||||
# 若删除节点为根节点,则重新指定根节点
|
||||
self.root = child
|
||||
self.__root = child
|
||||
# 子节点数量 = 2
|
||||
else:
|
||||
# 获取中序遍历中 cur 的下一个节点
|
||||
@ -1005,10 +1024,10 @@ comments: true
|
||||
|
||||
```javascript title="binary_search_tree.js"
|
||||
/* 删除节点 */
|
||||
function remove(num) {
|
||||
remove(num) {
|
||||
// 若树为空,直接提前返回
|
||||
if (root === null) return;
|
||||
let cur = root,
|
||||
if (this.root === null) return;
|
||||
let cur = this.root,
|
||||
pre = null;
|
||||
// 循环查找,越过叶节点后跳出
|
||||
while (cur !== null) {
|
||||
@ -1027,12 +1046,12 @@ comments: true
|
||||
// 当子节点数量 = 0 / 1 时, child = null / 该子节点
|
||||
let child = cur.left !== null ? cur.left : cur.right;
|
||||
// 删除节点 cur
|
||||
if (cur != root) {
|
||||
if (cur !== this.root) {
|
||||
if (pre.left === cur) pre.left = child;
|
||||
else pre.right = child;
|
||||
} else {
|
||||
// 若删除节点为根节点,则重新指定根节点
|
||||
root = child;
|
||||
this.root = child;
|
||||
}
|
||||
}
|
||||
// 子节点数量 = 2
|
||||
@ -1043,7 +1062,7 @@ comments: true
|
||||
tmp = tmp.left;
|
||||
}
|
||||
// 递归删除节点 tmp
|
||||
remove(tmp.val);
|
||||
this.remove(tmp.val);
|
||||
// 用 tmp 覆盖 cur
|
||||
cur.val = tmp.val;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user