From 00adffaca730c2228f84ad9b12988821155d55b1 Mon Sep 17 00:00:00 2001 From: krahets Date: Thu, 31 Aug 2023 02:30:38 +0800 Subject: [PATCH] build --- chapter_array_and_linkedlist/array.md | 7 +- chapter_array_and_linkedlist/summary.md | 7 +- .../dp_problem_features.md | 22 ++--- chapter_hashing/hash_algorithm.md | 4 +- chapter_stack_and_queue/queue.md | 6 +- chapter_tree/binary_search_tree.md | 97 +++++++++++-------- 6 files changed, 79 insertions(+), 64 deletions(-) diff --git a/chapter_array_and_linkedlist/array.md b/chapter_array_and_linkedlist/array.md index dc7c3c0be..0d505d7a2 100755 --- a/chapter_array_and_linkedlist/array.md +++ b/chapter_array_and_linkedlist/array.md @@ -120,12 +120,7 @@ comments: true ### 2.   访问元素 -数组元素被存储在连续的内存空间中,这意味着计算数组元素的内存地址非常容易。给定数组内存地址(即首元素内存地址)和某个元素的索引,我们可以使用以下公式计算得到该元素的内存地址,从而直接访问此元素。 - -```shell -# 元素内存地址 = 数组内存地址(首元素内存地址) + 元素长度 * 元素索引 -elementAddr = firtstElementAddr + elementLength * elementIndex -``` +数组元素被存储在连续的内存空间中,这意味着计算数组元素的内存地址非常容易。给定数组内存地址(即首元素内存地址)和某个元素的索引,我们可以使用图 4-2 所示的公式计算得到该元素的内存地址,从而直接访问此元素。 ![数组元素的内存地址计算](array.assets/array_memory_location_calculation.png) diff --git a/chapter_array_and_linkedlist/summary.md b/chapter_array_and_linkedlist/summary.md index edebdaf2e..66a573232 100644 --- a/chapter_array_and_linkedlist/summary.md +++ b/chapter_array_and_linkedlist/summary.md @@ -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}$ 呢?" diff --git a/chapter_dynamic_programming/dp_problem_features.md b/chapter_dynamic_programming/dp_problem_features.md index e09c009bd..ca7fdf103 100644 --- a/chapter_dynamic_programming/dp_problem_features.md +++ b/chapter_dynamic_programming/dp_problem_features.md @@ -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> dp(n + 1, vector(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> 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]; // 初始状态:预设最小子问题的解 diff --git a/chapter_hashing/hash_algorithm.md b/chapter_hashing/hash_algorithm.md index d620ae297..3275803b1 100644 --- a/chapter_hashing/hash_algorithm.md +++ b/chapter_hashing/hash_algorithm.md @@ -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 系列。 +

表 6-2   常见的哈希算法

+
| | MD5 | SHA-1 | SHA-2 | SHA-3 | diff --git a/chapter_stack_and_queue/queue.md b/chapter_stack_and_queue/queue.md index 3415cadb2..7083862c5 100755 --- a/chapter_stack_and_queue/queue.md +++ b/chapter_stack_and_queue/queue.md @@ -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)$ 。 diff --git a/chapter_tree/binary_search_tree.md b/chapter_tree/binary_search_tree.md index bd92c61ed..85b4c821d 100755 --- a/chapter_tree/binary_search_tree.md +++ b/chapter_tree/binary_search_tree.md @@ -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; }