From 799698e67c62a65bfdc16a26a1ad5ed367f83db9 Mon Sep 17 00:00:00 2001 From: krahets Date: Sat, 2 Sep 2023 16:10:28 +0800 Subject: [PATCH] build --- chapter_backtracking/permutations_problem.md | 2 +- .../iteration_and_recursion.md | 237 ++++++++++++++++-- chapter_searching/binary_search_edge.md | 26 +- chapter_searching/binary_search_insertion.md | 34 ++- chapter_tree/binary_search_tree.md | 3 +- 5 files changed, 274 insertions(+), 28 deletions(-) diff --git a/chapter_backtracking/permutations_problem.md b/chapter_backtracking/permutations_problem.md index ae9f784b6..d6d73abff 100644 --- a/chapter_backtracking/permutations_problem.md +++ b/chapter_backtracking/permutations_problem.md @@ -901,7 +901,7 @@ comments: true } ``` -假设元素两两之间互不相同,则 $n$ 个元素共有 $n!$ 种排列(阶乘);在记录结果时,需要复制长度为 $n$ 的列表,使用 $O(n)$ 时间。因此,**时间复杂度为 $O(n!n)$** 。 +假设元素两两之间互不相同,则 $n$ 个元素共有 $n!$ 种排列(阶乘);在记录结果时,需要复制长度为 $n$ 的列表,使用 $O(n)$ 时间。**因此时间复杂度为 $O(n!n)$** 。 最大递归深度为 $n$ ,使用 $O(n)$ 栈帧空间。`selected` 使用 $O(n)$ 空间。同一时刻最多共有 $n$ 个 `duplicated` ,使用 $O(n^2)$ 空间。**因此空间复杂度为 $O(n^2)$** 。 diff --git a/chapter_computational_complexity/iteration_and_recursion.md b/chapter_computational_complexity/iteration_and_recursion.md index c51cbc34a..44928c81f 100644 --- a/chapter_computational_complexity/iteration_and_recursion.md +++ b/chapter_computational_complexity/iteration_and_recursion.md @@ -74,13 +74,29 @@ status: new === "JS" ```javascript title="iteration.js" - [class]{}-[func]{forLoop} + /* for 循环 */ + function forLoop(n) { + let res = 0; + // 循环求和 1, 2, ..., n-1, n + for (let i = 1; i <= n; i++) { + res += i; + } + return res; + } ``` === "TS" ```typescript title="iteration.ts" - [class]{}-[func]{forLoop} + /* for 循环 */ + function forLoop(n: number): number { + let res = 0; + // 循环求和 1, 2, ..., n-1, n + for (let i = 1; i <= n; i++) { + res += i; + } + return res; + } ``` === "C" @@ -132,7 +148,15 @@ status: new === "Rust" ```rust title="iteration.rs" - [class]{}-[func]{for_loop} + /* for 循环 */ + fn for_loop(n: i32) -> i32 { + let mut res = 0; + // 循环求和 1, 2, ..., n-1, n + for i in 1..=n { + res += i; + } + res + } ``` 图 2-1 展示了该求和函数的流程框图。 @@ -216,13 +240,33 @@ status: new === "JS" ```javascript title="iteration.js" - [class]{}-[func]{whileLoop} + /* while 循环 */ + function whileLoop(n) { + let res = 0; + let i = 1; // 初始化条件变量 + // 循环求和 1, 2, ..., n-1, n + while (i <= n) { + res += i; + i++; // 更新条件变量 + } + return res; + } ``` === "TS" ```typescript title="iteration.ts" - [class]{}-[func]{whileLoop} + /* while 循环 */ + function whileLoop(n: number): number { + let res = 0; + let i = 1; // 初始化条件变量 + // 循环求和 1, 2, ..., n-1, n + while (i <= n) { + res += i; + i++; // 更新条件变量 + } + return res; + } ``` === "C" @@ -278,7 +322,17 @@ status: new === "Rust" ```rust title="iteration.rs" - [class]{}-[func]{while_loop} + /* while 循环 */ + fn while_loop(n: i32) -> i32 { + let mut res = 0; + let mut i = 1; // 初始化条件变量 + // 循环求和 1, 2, ..., n-1, n + while i <= n { + res += i; + i += 1; // 更新条件变量 + } + res + } ``` 在 `while` 循环中,由于初始化和更新条件变量的步骤是独立在循环结构之外的,**因此它比 `for` 循环的自由度更高**。 @@ -359,13 +413,37 @@ status: new === "JS" ```javascript title="iteration.js" - [class]{}-[func]{whileLoopII} + /* while 循环(两次更新) */ + function whileLoopII(n) { + let res = 0; + let i = 1; // 初始化条件变量 + // 循环求和 1, 4, ... + while (i <= n) { + res += i; + // 更新条件变量 + i++; + i *= 2; + } + return res; + } ``` === "TS" ```typescript title="iteration.ts" - [class]{}-[func]{whileLoopII} + /* while 循环(两次更新) */ + function whileLoopII(n: number): number { + let res = 0; + let i = 1; // 初始化条件变量 + // 循环求和 1, 4, ... + while (i <= n) { + res += i; + // 更新条件变量 + i++; + i *= 2; + } + return res; + } ``` === "C" @@ -425,7 +503,19 @@ status: new === "Rust" ```rust title="iteration.rs" - [class]{}-[func]{while_loop_ii} + /* while 循环(两次更新) */ + fn while_loop_ii(n: i32) -> i32 { + let mut res = 0; + let mut i = 1; // 初始化条件变量 + // 循环求和 1, 4, ... + while i <= n { + res += i; + // 更新条件变量 + i += 1; + i *= 2; + } + res + } ``` 总的来说,**`for` 循环的代码更加紧凑,`while` 循环更加灵活**,两者都可以实现迭代结构。选择使用哪一个应该根据特定问题的需求来决定。 @@ -502,13 +592,35 @@ status: new === "JS" ```javascript title="iteration.js" - [class]{}-[func]{nestedForLoop} + /* 双层 for 循环 */ + function nestedForLoop(n) { + let res = ''; + // 循环 i = 1, 2, ..., n-1, n + for (let i = 1; i <= n; i++) { + // 循环 j = 1, 2, ..., n-1, n + for (let j = 1; j <= n; j++) { + res += `(${i}, ${j}), `; + } + } + return res; + } ``` === "TS" ```typescript title="iteration.ts" - [class]{}-[func]{nestedForLoop} + /* 双层 for 循环 */ + function nestedForLoop(n: number): string { + let res = ''; + // 循环 i = 1, 2, ..., n-1, n + for (let i = 1; i <= n; i++) { + // 循环 j = 1, 2, ..., n-1, n + for (let j = 1; j <= n; j++) { + res += `(${i}, ${j}), `; + } + } + return res; + } ``` === "C" @@ -566,7 +678,18 @@ status: new === "Rust" ```rust title="iteration.rs" - [class]{}-[func]{nested_for_loop} + /* 双层 for 循环 */ + fn nested_for_loop(n: i32) -> String { + let mut res = vec![]; + // 循环 i = 1, 2, ..., n-1, n + for i in 1..=n { + // 循环 j = 1, 2, ..., n-1, n + for j in 1..=n { + res.push(format!("({}, {}), ", i, j)); + } + } + res.join("") + } ``` 图 2-2 给出了该嵌套循环的流程框图。 @@ -657,13 +780,29 @@ status: new === "JS" ```javascript title="recursion.js" - [class]{}-[func]{recur} + /* 递归 */ + function recur(n) { + // 终止条件 + if (n === 1) return 1; + // 递:递归调用 + const res = recur(n - 1); + // 归:返回结果 + return n + res; + } ``` === "TS" ```typescript title="recursion.ts" - [class]{}-[func]{recur} + /* 递归 */ + function recur(n: number): number { + // 终止条件 + if (n === 1) return 1; + // 递:递归调用 + const res = recur(n - 1); + // 归:返回结果 + return n + res; + } ``` === "C" @@ -716,7 +855,17 @@ status: new === "Rust" ```rust title="recursion.rs" - [class]{}-[func]{recur} + /* 递归 */ + fn recur(n: i32) -> i32 { + // 终止条件 + if n == 1 { + return 1; + } + // 递:递归调用 + let res = recur(n - 1); + // 归:返回结果 + n + res + } ``` 图 2-3 展示了该函数的递归过程。 @@ -814,13 +963,25 @@ status: new === "JS" ```javascript title="recursion.js" - [class]{}-[func]{tailRecur} + /* 尾递归 */ + function tailRecur(n, res) { + // 终止条件 + if (n === 0) return res; + // 尾递归调用 + return tailRecur(n - 1, res + n); + } ``` === "TS" ```typescript title="recursion.ts" - [class]{}-[func]{tailRecur} + /* 尾递归 */ + function tailRecur(n: number, res: number): number { + // 终止条件 + if (n === 0) return res; + // 尾递归调用 + return tailRecur(n - 1, res + n); + } ``` === "C" @@ -869,7 +1030,15 @@ status: new === "Rust" ```rust title="recursion.rs" - [class]{}-[func]{tail_recur} + /* 尾递归 */ + fn tail_recur(n: i32, res: i32) -> i32 { + // 终止条件 + if n == 0 { + return res; + } + // 尾递归调用 + tail_recur(n - 1, res + n) + } ``` 两种递归的过程对比如图 2-5 所示。 @@ -961,13 +1130,29 @@ status: new === "JS" ```javascript title="recursion.js" - [class]{}-[func]{fib} + /* 斐波那契数列:递归 */ + function fib(n) { + // 终止条件 f(1) = 0, f(2) = 1 + if (n === 1 || n === 2) return n - 1; + // 递归调用 f(n) = f(n-1) + f(n-2) + const res = fib(n - 1) + fib(n - 2); + // 返回结果 f(n) + return res; + } ``` === "TS" ```typescript title="recursion.ts" - [class]{}-[func]{fib} + /* 斐波那契数列:递归 */ + function fib(n: number): number { + // 终止条件 f(1) = 0, f(2) = 1 + if (n === 1 || n === 2) return n - 1; + // 递归调用 f(n) = f(n-1) + f(n-2) + const res = fib(n - 1) + fib(n - 2); + // 返回结果 f(n) + return res; + } ``` === "C" @@ -1020,7 +1205,17 @@ status: new === "Rust" ```rust title="recursion.rs" - [class]{}-[func]{fib} + /* 斐波那契数列:递归 */ + fn fib(n: i32) -> i32 { + // 终止条件 f(1) = 0, f(2) = 1 + if n == 1 || n == 2 { + return n - 1; + } + // 递归调用 f(n) = f(n-1) + f(n-2) + let res = fib(n - 1) + fib(n - 2); + // 返回结果 + res + } ``` 观察以上代码,我们在函数内递归调用了两个函数,**这意味着从一个调用产生了两个调用分支**。如图 2-6 所示,这样不断递归调用下去,最终将产生一个层数为 $n$ 的「递归树 recursion tree」。 diff --git a/chapter_searching/binary_search_edge.md b/chapter_searching/binary_search_edge.md index d7518e5b1..5ada581ae 100644 --- a/chapter_searching/binary_search_edge.md +++ b/chapter_searching/binary_search_edge.md @@ -177,7 +177,17 @@ status: new === "Rust" ```rust title="binary_search_edge.rs" - [class]{}-[func]{binary_search_left_edge} + /* 二分查找最左一个 target */ + fn binary_search_left_edge(nums: &[i32], target: i32) -> i32 { + // 等价于查找 target 的插入点 + let i = binary_search_insertion(nums, target); + // 未找到 target ,返回 -1 + if i == nums.len() as i32 || nums[i as usize] != target { + return -1; + } + // 找到 target ,返回索引 i + i + } ``` ## 10.3.2   查找右边界 @@ -373,7 +383,19 @@ status: new === "Rust" ```rust title="binary_search_edge.rs" - [class]{}-[func]{binary_search_right_edge} + /* 二分查找最右一个 target */ + fn binary_search_right_edge(nums: &[i32], target: i32) -> i32 { + // 转化为查找最左一个 target + 1 + let i = binary_search_insertion(nums, target + 1); + // j 指向最右一个 target ,i 指向首个大于 target 的元素 + let j = i - 1; + // 未找到 target ,返回 -1 + if j == -1 || nums[j as usize] != target { + return -1; + } + // 找到 target ,返回索引 j + j + } ``` ### 2.   转化为查找元素 diff --git a/chapter_searching/binary_search_insertion.md b/chapter_searching/binary_search_insertion.md index 22652b4b9..889db664f 100644 --- a/chapter_searching/binary_search_insertion.md +++ b/chapter_searching/binary_search_insertion.md @@ -237,7 +237,22 @@ status: new === "Rust" ```rust title="binary_search_insertion.rs" - [class]{}-[func]{binary_search_insertion} + /* 二分查找插入点(存在重复元素) */ + pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 { + let (mut i, mut j) = (0, nums.len() as i32 - 1); // 初始化双闭区间 [0, n-1] + while i <= j { + let m = i + (j - i) / 2; // 计算中点索引 m + if nums[m as usize] < target { + i = m + 1; // target 在区间 [m+1, j] 中 + } else if nums[m as usize] > target { + j = m - 1; // target 在区间 [i, m-1] 中 + } else { + j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 + } + } + // 返回插入点 i + i + } ``` ## 10.2.2   存在重复元素的情况 @@ -504,7 +519,22 @@ status: new === "Rust" ```rust title="binary_search_insertion.rs" - [class]{}-[func]{binary_search_insertion} + /* 二分查找插入点(存在重复元素) */ + pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 { + let (mut i, mut j) = (0, nums.len() as i32 - 1); // 初始化双闭区间 [0, n-1] + while i <= j { + let m = i + (j - i) / 2; // 计算中点索引 m + if nums[m as usize] < target { + i = m + 1; // target 在区间 [m+1, j] 中 + } else if nums[m as usize] > target { + j = m - 1; // target 在区间 [i, m-1] 中 + } else { + j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 + } + } + // 返回插入点 i + i + } ``` !!! tip diff --git a/chapter_tree/binary_search_tree.md b/chapter_tree/binary_search_tree.md index 85b4c821d..7d0652e80 100755 --- a/chapter_tree/binary_search_tree.md +++ b/chapter_tree/binary_search_tree.md @@ -715,7 +715,7 @@ comments: true pub fn insert(&mut self, num: i32) { // 若树为空,则初始化根节点 if self.root.is_none() { - self.root = TreeNode::new(num); + self.root = Some(TreeNode::new(num)); return; } let mut cur = self.root.clone(); @@ -1356,7 +1356,6 @@ comments: true void remove(int num) { // 若树为空,直接提前返回 if (_root == null) return; - TreeNode? cur = _root; TreeNode? pre = null; // 循环查找,越过叶节点后跳出