diff --git a/codes/c/chapter_searching/binary_search_edge.c b/codes/c/chapter_searching/binary_search_edge.c index 994f2a913..f2bb1bb56 100644 --- a/codes/c/chapter_searching/binary_search_edge.c +++ b/codes/c/chapter_searching/binary_search_edge.c @@ -14,7 +14,7 @@ int binarySearchInsertion(int *nums, int numSize, int target) { if (nums[m] < target) { i = m + 1; // target 在区间 [m+1, j] 中 } else { - j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1; // 最右一个小于 target 的元素在区间 [i, m-1] 中 } } // 返回插入点 i @@ -64,4 +64,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/codes/c/chapter_searching/binary_search_insertion.c b/codes/c/chapter_searching/binary_search_insertion.c index 48d2025d3..54235ea26 100644 --- a/codes/c/chapter_searching/binary_search_insertion.c +++ b/codes/c/chapter_searching/binary_search_insertion.c @@ -33,7 +33,7 @@ int binarySearchInsertion(int *nums, int numSize, int target) { } else if (nums[m] > target) { j = m - 1; // target 在区间 [i, m-1] 中 } else { - j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1; // 最右一个小于 target 的元素在区间 [i, m-1] 中 } } // 返回插入点 i diff --git a/codes/cpp/chapter_hashing/built_in_hash.cpp b/codes/cpp/chapter_hashing/built_in_hash.cpp index da3f90c75..1c06c77e6 100644 --- a/codes/cpp/chapter_hashing/built_in_hash.cpp +++ b/codes/cpp/chapter_hashing/built_in_hash.cpp @@ -24,6 +24,6 @@ int main() { size_t hashStr = hash()(str); cout << "字符串 " << str << " 的哈希值为 " << hashStr << "\n"; - // 在 C++ 中,内置 std:hash() 仅提供基本数据类型的哈希值计算 + // 在 C++ 中,内置 std::hash() 仅提供基本数据类型的哈希值计算 // 数组、对象的哈希值计算需要自行实现 } diff --git a/codes/cpp/chapter_searching/binary_search_edge.cpp b/codes/cpp/chapter_searching/binary_search_edge.cpp index 6a536c232..ae955b3a1 100644 --- a/codes/cpp/chapter_searching/binary_search_edge.cpp +++ b/codes/cpp/chapter_searching/binary_search_edge.cpp @@ -14,7 +14,7 @@ int binarySearchInsertion(const vector &nums, int target) { if (nums[m] < target) { i = m + 1; // target 在区间 [m+1, j] 中 } else { - j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1; // 最右一个小于 target 的元素在区间 [i, m-1] 中 } } // 返回插入点 i diff --git a/codes/cpp/chapter_searching/binary_search_insertion.cpp b/codes/cpp/chapter_searching/binary_search_insertion.cpp index f2fd15a38..fb1d96067 100644 --- a/codes/cpp/chapter_searching/binary_search_insertion.cpp +++ b/codes/cpp/chapter_searching/binary_search_insertion.cpp @@ -33,7 +33,7 @@ int binarySearchInsertion(vector &nums, int target) { } else if (nums[m] > target) { j = m - 1; // target 在区间 [i, m-1] 中 } else { - j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1; // 最右一个小于 target 的元素在区间 [i, m-1] 中 } } // 返回插入点 i diff --git a/codes/csharp/chapter_searching/binary_search_insertion.cs b/codes/csharp/chapter_searching/binary_search_insertion.cs index af4c1d21f..e4b10bfbc 100644 --- a/codes/csharp/chapter_searching/binary_search_insertion.cs +++ b/codes/csharp/chapter_searching/binary_search_insertion.cs @@ -34,7 +34,7 @@ public class binary_search_insertion { } else if (nums[m] > target) { j = m - 1; // target 在区间 [i, m-1] 中 } else { - j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1; // 最右一个小于 target 的元素在区间 [i, m-1] 中 } } // 返回插入点 i diff --git a/codes/dart/chapter_searching/binary_search_insertion.dart b/codes/dart/chapter_searching/binary_search_insertion.dart index 141c80b62..9c11da32e 100644 --- a/codes/dart/chapter_searching/binary_search_insertion.dart +++ b/codes/dart/chapter_searching/binary_search_insertion.dart @@ -31,7 +31,7 @@ int binarySearchInsertion(List nums, int target) { } else if (nums[m] > target) { j = m - 1; // target 在区间 [i, m-1] 中 } else { - j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1; // 最右一个小于 target 的元素在区间 [i, m-1] 中 } } // 返回插入点 i diff --git a/codes/go/chapter_searching/binary_search_insertion.go b/codes/go/chapter_searching/binary_search_insertion.go index ffd413032..b8d543f25 100644 --- a/codes/go/chapter_searching/binary_search_insertion.go +++ b/codes/go/chapter_searching/binary_search_insertion.go @@ -40,7 +40,7 @@ func binarySearchInsertion(nums []int, target int) int { // target 在区间 [i, m-1] 中 j = m - 1 } else { - // 首个小于 target 的元素在区间 [i, m-1] 中 + // 最右一个小于 target 的元素在区间 [i, m-1] 中 j = m - 1 } } diff --git a/codes/java/chapter_searching/binary_search_insertion.java b/codes/java/chapter_searching/binary_search_insertion.java index c6c753a96..4f5bd1a4b 100644 --- a/codes/java/chapter_searching/binary_search_insertion.java +++ b/codes/java/chapter_searching/binary_search_insertion.java @@ -34,7 +34,7 @@ class binary_search_insertion { } else if (nums[m] > target) { j = m - 1; // target 在区间 [i, m-1] 中 } else { - j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1; // 最右一个小于 target 的元素在区间 [i, m-1] 中 } } // 返回插入点 i diff --git a/codes/javascript/chapter_searching/binary_search_insertion.js b/codes/javascript/chapter_searching/binary_search_insertion.js index af6544211..bc84d0e29 100644 --- a/codes/javascript/chapter_searching/binary_search_insertion.js +++ b/codes/javascript/chapter_searching/binary_search_insertion.js @@ -33,7 +33,7 @@ function binarySearchInsertion(nums, target) { } else if (nums[m] > target) { j = m - 1; // target 在区间 [i, m-1] 中 } else { - j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1; // 最右一个小于 target 的元素在区间 [i, m-1] 中 } } // 返回插入点 i diff --git a/codes/kotlin/chapter_searching/binary_search_insertion.kt b/codes/kotlin/chapter_searching/binary_search_insertion.kt index 7c5279e6b..ee6b3227f 100644 --- a/codes/kotlin/chapter_searching/binary_search_insertion.kt +++ b/codes/kotlin/chapter_searching/binary_search_insertion.kt @@ -35,7 +35,7 @@ fun binarySearchInsertion(nums: IntArray, target: Int): Int { } else if (nums[m] > target) { j = m - 1 // target 在区间 [i, m-1] 中 } else { - j = m - 1 // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1 // 最右一个小于 target 的元素在区间 [i, m-1] 中 } } // 返回插入点 i @@ -62,4 +62,4 @@ fun main() { val index = binarySearchInsertion(nums, target) println("元素 $target 的插入点的索引为 $index") } -} \ No newline at end of file +} diff --git a/codes/python/chapter_searching/binary_search_insertion.py b/codes/python/chapter_searching/binary_search_insertion.py index 1ba0b956f..de9999205 100644 --- a/codes/python/chapter_searching/binary_search_insertion.py +++ b/codes/python/chapter_searching/binary_search_insertion.py @@ -30,7 +30,7 @@ def binary_search_insertion(nums: list[int], target: int) -> int: elif nums[m] > target: j = m - 1 # target 在区间 [i, m-1] 中 else: - j = m - 1 # 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1 # 最右一个小于 target 的元素在区间 [i, m-1] 中 # 返回插入点 i return i diff --git a/codes/ruby/chapter_backtracking/permutations_ii.rb b/codes/ruby/chapter_backtracking/permutations_ii.rb index ee3e84cd9..a440b95d2 100644 --- a/codes/ruby/chapter_backtracking/permutations_ii.rb +++ b/codes/ruby/chapter_backtracking/permutations_ii.rb @@ -4,6 +4,8 @@ Created Time: 2024-05-22 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end +require 'set' + ### 回溯算法:全排列 II ### def backtrack(state, choices, selected, res) # 当状态长度等于元素数量时,记录解 diff --git a/codes/ruby/chapter_searching/binary_search_insertion.rb b/codes/ruby/chapter_searching/binary_search_insertion.rb index 5a0ee18cc..985f53b4b 100644 --- a/codes/ruby/chapter_searching/binary_search_insertion.rb +++ b/codes/ruby/chapter_searching/binary_search_insertion.rb @@ -39,7 +39,7 @@ def binary_search_insertion(nums, target) elsif nums[m] > target j = m - 1 # target 在区间 [i, m-1] 中 else - j = m - 1 # 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1 # 最右一个小于 target 的元素在区间 [i, m-1] 中 end end diff --git a/codes/rust/chapter_searching/binary_search_insertion.rs b/codes/rust/chapter_searching/binary_search_insertion.rs index d0c584e3c..76a851fdb 100644 --- a/codes/rust/chapter_searching/binary_search_insertion.rs +++ b/codes/rust/chapter_searching/binary_search_insertion.rs @@ -32,7 +32,7 @@ pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 { } else if nums[m as usize] > target { j = m - 1; // target 在区间 [i, m-1] 中 } else { - j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1; // 最右一个小于 target 的元素在区间 [i, m-1] 中 } } // 返回插入点 i diff --git a/codes/swift/chapter_searching/binary_search_insertion.swift b/codes/swift/chapter_searching/binary_search_insertion.swift index ced9f1d87..e4734c353 100644 --- a/codes/swift/chapter_searching/binary_search_insertion.swift +++ b/codes/swift/chapter_searching/binary_search_insertion.swift @@ -35,7 +35,7 @@ public func binarySearchInsertion(nums: [Int], target: Int) -> Int { } else if nums[m] > target { j = m - 1 // target 在区间 [i, m-1] 中 } else { - j = m - 1 // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1 // 最右一个小于 target 的元素在区间 [i, m-1] 中 } } // 返回插入点 i diff --git a/codes/swift/chapter_stack_and_queue/queue.swift b/codes/swift/chapter_stack_and_queue/queue.swift index e7b746e35..84ef7f8fe 100644 --- a/codes/swift/chapter_stack_and_queue/queue.swift +++ b/codes/swift/chapter_stack_and_queue/queue.swift @@ -26,8 +26,8 @@ enum Queue { /* 元素出队 */ // 使用 Array 模拟时 pop 的复杂度为 O(n) - let pool = queue.removeFirst() - print("出队元素 pop = \(pool),出队后 queue = \(queue)") + let pop = queue.removeFirst() + print("出队元素 pop = \(pop),出队后 queue = \(queue)") /* 获取队列的长度 */ let size = queue.count diff --git a/codes/typescript/chapter_searching/binary_search_insertion.ts b/codes/typescript/chapter_searching/binary_search_insertion.ts index bb470e28d..05b3a632c 100644 --- a/codes/typescript/chapter_searching/binary_search_insertion.ts +++ b/codes/typescript/chapter_searching/binary_search_insertion.ts @@ -36,7 +36,7 @@ function binarySearchInsertion(nums: Array, target: number): number { } else if (nums[m] > target) { j = m - 1; // target 在区间 [i, m-1] 中 } else { - j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1; // 最右一个小于 target 的元素在区间 [i, m-1] 中 } } // 返回插入点 i diff --git a/docs/chapter_appendix/installation.md b/docs/chapter_appendix/installation.md index b0722afcd..f214d223e 100644 --- a/docs/chapter_appendix/installation.md +++ b/docs/chapter_appendix/installation.md @@ -20,7 +20,7 @@ VS Code 拥有强大的扩展包生态系统,支持大多数编程语言的运 ### C/C++ 环境 -1. Windows 系统需要安装 [MinGW](https://sourceforge.net/projects/mingw-w64/files/)([配置教程](https://blog.csdn.net/qq_33698226/article/details/129031241));MacOS 自带 Clang ,无须安装。 +1. Windows 系统需要安装 [MinGW](https://sourceforge.net/projects/mingw-w64/files/)([配置教程](https://blog.csdn.net/qq_33698226/article/details/129031241));macOS 自带 Clang ,无须安装。 2. 在 VS Code 的插件市场中搜索 `c++` ,安装 C/C++ Extension Pack 。 3. (可选)打开 Settings 页面,搜索 `Clang_format_fallback Style` 代码格式化选项,设置为 `{ BasedOnStyle: Microsoft, BreakBeforeBraces: Attach }` 。 @@ -31,7 +31,7 @@ VS Code 拥有强大的扩展包生态系统,支持大多数编程语言的运 ### C# 环境 -1. 下载并安装 [.Net 8.0](https://dotnet.microsoft.com/en-us/download) 。 +1. 下载并安装 [.NET 8.0](https://dotnet.microsoft.com/en-us/download) 。 2. 在 VS Code 的插件市场中搜索 `C# Dev Kit` ,安装 C# Dev Kit ([配置教程](https://code.visualstudio.com/docs/csharp/get-started))。 3. 也可使用 Visual Studio([安装教程](https://learn.microsoft.com/zh-cn/visualstudio/install/install-visual-studio?view=vs-2022))。 diff --git a/docs/chapter_appendix/terminology.md b/docs/chapter_appendix/terminology.md index 82fb6455c..bc82555d7 100644 --- a/docs/chapter_appendix/terminology.md +++ b/docs/chapter_appendix/terminology.md @@ -80,7 +80,9 @@ | level-order traversal | 层序遍历 | | breadth-first traversal | 广度优先遍历 | | depth-first traversal | 深度优先遍历 | -| binary search tree | 二叉搜索树 | +| pre-order traversal | 前序遍历 | +| in-order traversal | 中序遍历 | +| post-order traversal | 后序遍历 | | balanced binary search tree | 平衡二叉搜索树 | | balance factor | 平衡因子 | | heap | 堆 | diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index 910e22033..0a64e5122 100755 --- a/docs/chapter_array_and_linkedlist/linked_list.md +++ b/docs/chapter_array_and_linkedlist/linked_list.md @@ -166,7 +166,7 @@ // 构造方法 class ListNode(x: Int) { val _val: Int = x // 节点值 - val next: ListNode? = null // 指向下一个节点的引用 + var next: ListNode? = null // 指向下一个节点的引用 } ``` @@ -658,8 +658,8 @@ // 构造方法 class ListNode(x: Int) { val _val: Int = x // 节点值 - val next: ListNode? = null // 指向后继节点的引用 - val prev: ListNode? = null // 指向前驱节点的引用 + var next: ListNode? = null // 指向后继节点的引用 + var prev: ListNode? = null // 指向前驱节点的引用 } ``` diff --git a/docs/chapter_backtracking/permutations_problem.md b/docs/chapter_backtracking/permutations_problem.md index 4c993606a..f1bbbdf07 100644 --- a/docs/chapter_backtracking/permutations_problem.md +++ b/docs/chapter_backtracking/permutations_problem.md @@ -30,7 +30,7 @@ 为了实现每个元素只被选择一次,我们考虑引入一个布尔型数组 `selected` ,其中 `selected[i]` 表示 `choices[i]` 是否已被选择,并基于它实现以下剪枝操作。 -- 在做出选择 `choice[i]` 后,我们就将 `selected[i]` 赋值为 $\text{True}$ ,代表它已被选择。 +- 在做出选择 `choices[i]` 后,我们就将 `selected[i]` 赋值为 $\text{True}$ ,代表它已被选择。 - 遍历选择列表 `choices` 时,跳过所有已被选择的节点,即剪枝。 如下图所示,假设我们第一轮选择 1 ,第二轮选择 3 ,第三轮选择 2 ,则需要在第二轮剪掉元素 1 的分支,在第三轮剪掉元素 1 和元素 3 的分支。 diff --git a/docs/chapter_data_structure/summary.md b/docs/chapter_data_structure/summary.md index 54af46600..86c7eaefa 100644 --- a/docs/chapter_data_structure/summary.md +++ b/docs/chapter_data_structure/summary.md @@ -10,7 +10,7 @@ - 原码、反码和补码是在计算机中编码数字的三种方法,它们之间可以相互转换。整数的原码的最高位是符号位,其余位是数字的值。 - 整数在计算机中是以补码的形式存储的。在补码表示下,计算机可以对正数和负数的加法一视同仁,不需要为减法操作单独设计特殊的硬件电路,并且不存在正负零歧义的问题。 - 浮点数的编码由 1 位符号位、8 位指数位和 23 位分数位构成。由于存在指数位,因此浮点数的取值范围远大于整数,代价是牺牲了精度。 -- ASCII 码是最早出现的英文字符集,长度为 1 字节,共收录 127 个字符。GBK 字符集是常用的中文字符集,共收录两万多个汉字。Unicode 致力于提供一个完整的字符集标准,收录世界上各种语言的字符,从而解决由于字符编码方法不一致而导致的乱码问题。 +- ASCII 码是最早出现的英文字符集,长度为 1 字节,共收录 128 个字符。GBK 字符集是常用的中文字符集,共收录两万多个汉字。Unicode 致力于提供一个完整的字符集标准,收录世界上各种语言的字符,从而解决由于字符编码方法不一致而导致的乱码问题。 - UTF-8 是最受欢迎的 Unicode 编码方法,通用性非常好。它是一种变长的编码方法,具有很好的扩展性,有效提升了存储空间的使用效率。UTF-16 和 UTF-32 是等长的编码方法。在编码中文时,UTF-16 占用的空间比 UTF-8 更小。Java 和 C# 等编程语言默认使用 UTF-16 编码。 ### Q & A diff --git a/docs/chapter_divide_and_conquer/build_binary_tree_problem.md b/docs/chapter_divide_and_conquer/build_binary_tree_problem.md index 62e06eca7..aeabde419 100644 --- a/docs/chapter_divide_and_conquer/build_binary_tree_problem.md +++ b/docs/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -21,12 +21,12 @@ 根据定义,`preorder` 和 `inorder` 都可以划分为三个部分。 - 前序遍历:`[ 根节点 | 左子树 | 右子树 ]` ,例如上图的树对应 `[ 3 | 9 | 2 1 7 ]` 。 -- 中序遍历:`[ 左子树 | 根节点 | 右子树 ]` ,例如上图的树对应 `[ 9 | 3 | 1 2 7 ]` 。 +- 中序遍历:`[ 左子树 | 根节点 | 右子树 ]` ,例如上图的树对应 `[ 9 | 3 | 1 2 7 ]` 。 以上图数据为例,我们可以通过下图所示的步骤得到划分结果。 1. 前序遍历的首元素 3 是根节点的值。 -2. 查找根节点 3 在 `inorder` 中的索引,利用该索引可将 `inorder` 划分为 `[ 9 | 3 | 1 2 7 ]` 。 +2. 查找根节点 3 在 `inorder` 中的索引,利用该索引可将 `inorder` 划分为 `[ 9 | 3 | 1 2 7 ]` 。 3. 根据 `inorder` 的划分结果,易得左子树和右子树的节点数量分别为 1 和 3 ,从而可将 `preorder` 划分为 `[ 3 | 9 | 2 1 7 ]` 。 ![在前序遍历和中序遍历中划分子树](build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png) diff --git a/docs/chapter_dynamic_programming/edit_distance_problem.md b/docs/chapter_dynamic_programming/edit_distance_problem.md index d3a318455..38687a270 100644 --- a/docs/chapter_dynamic_programming/edit_distance_problem.md +++ b/docs/chapter_dynamic_programming/edit_distance_problem.md @@ -35,7 +35,7 @@ 状态 $[i, j]$ 对应的子问题:**将 $s$ 的前 $i$ 个字符更改为 $t$ 的前 $j$ 个字符所需的最少编辑步数**。 -至此,得到一个尺寸为 $(i+1) \times (j+1)$ 的二维 $dp$ 表。 +至此,得到一个尺寸为 $(n+1) \times (m+1)$ 的二维 $dp$ 表。 **第二步:找出最优子结构,进而推导出状态转移方程** diff --git a/docs/chapter_dynamic_programming/knapsack_problem.md b/docs/chapter_dynamic_programming/knapsack_problem.md index f45d13b52..c3579d179 100644 --- a/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/docs/chapter_dynamic_programming/knapsack_problem.md @@ -134,7 +134,7 @@ $$ ### 空间优化 -由于每个状态都只与其上一行的状态有关,因此我们可以使用两个数组滚动前进,将空间复杂度从 $O(n^2)$ 降至 $O(n)$ 。 +由于每个状态都只与其上一行的状态有关,因此我们可以使用两个数组滚动前进,将空间复杂度从 $O(n \times cap)$ 降至 $O(cap)$ 。 进一步思考,我们能否仅用一个数组实现空间优化呢?观察可知,每个状态都是由正上方或左上方的格子转移过来的。假设只有一个数组,当开始遍历第 $i$ 行时,该数组存储的仍然是第 $i-1$ 行的状态。 diff --git a/docs/chapter_dynamic_programming/summary.md b/docs/chapter_dynamic_programming/summary.md index 34eaf3259..6c7cb5db1 100644 --- a/docs/chapter_dynamic_programming/summary.md +++ b/docs/chapter_dynamic_programming/summary.md @@ -21,5 +21,5 @@ **编辑距离问题** - 编辑距离(Levenshtein 距离)用于衡量两个字符串之间的相似度,其定义为从一个字符串到另一个字符串的最少编辑步数,编辑操作包括添加、删除、替换。 -- 编辑距离问题的状态定义为将 $s$ 的前 $i$ 个字符更改为 $t$ 的前 $j$ 个字符所需的最少编辑步数。当 $s[i] \ne t[j]$ 时,具有三种决策:添加、删除、替换,它们都有相应的剩余子问题。据此便可以找出最优子结构与构建状态转移方程。而当 $s[i] = t[j]$ 时,无须编辑当前字符。 +- 编辑距离问题的状态定义为将 $s$ 的前 $i$ 个字符更改为 $t$ 的前 $j$ 个字符所需的最少编辑步数。当 $s[i-1] \ne t[j-1]$ 时,具有三种决策:添加、删除、替换,它们都有相应的剩余子问题。据此便可以找出最优子结构与构建状态转移方程。而当 $s[i-1] = t[j-1]$ 时,无须编辑当前字符。 - 在编辑距离中,状态依赖其正上方、正左方、左上方的状态,因此空间优化后正序或倒序遍历都无法正确地进行状态转移。为此,我们利用一个变量暂存左上方状态,从而转化到与完全背包问题等价的情况,可以在空间优化后进行正序遍历。 diff --git a/docs/chapter_greedy/fractional_knapsack_problem.md b/docs/chapter_greedy/fractional_knapsack_problem.md index 33e064d85..c3141a83c 100644 --- a/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/docs/chapter_greedy/fractional_knapsack_problem.md @@ -33,7 +33,7 @@ [file]{fractional_knapsack}-[class]{}-[func]{fractional_knapsack} ``` -内置排序算法的时间复杂度通常为 $O(\log n)$ ,空间复杂度通常为 $O(\log n)$ 或 $O(n)$ ,取决于编程语言的具体实现。 +内置排序算法的时间复杂度通常为 $O(n \log n)$ ,空间复杂度通常为 $O(\log n)$ 或 $O(n)$ ,取决于编程语言的具体实现。 除排序之外,在最差情况下,需要遍历整个物品列表,**因此时间复杂度为 $O(n)$** ,其中 $n$ 为物品数量。 diff --git a/docs/chapter_greedy/greedy_algorithm.md b/docs/chapter_greedy/greedy_algorithm.md index 1a961fe72..178bbcd35 100644 --- a/docs/chapter_greedy/greedy_algorithm.md +++ b/docs/chapter_greedy/greedy_algorithm.md @@ -91,4 +91,4 @@ - **分数背包问题**:给定一组物品和一个载重量,你的目标是选择一组物品,使得总重量不超过载重量,且总价值最大。如果每次都选择性价比最高(价值 / 重量)的物品,那么贪心算法在一些情况下可以得到最优解。 - **股票买卖问题**:给定一组股票的历史价格,你可以进行多次买卖,但如果你已经持有股票,那么在卖出之前不能再买,目标是获取最大利润。 - **霍夫曼编码**:霍夫曼编码是一种用于无损数据压缩的贪心算法。通过构建霍夫曼树,每次选择出现频率最低的两个节点合并,最后得到的霍夫曼树的带权路径长度(编码长度)最小。 -- **Dijkstra 算法**:它是一种解决给定源顶点到其余各顶点的最短路径问题的贪心算法。 +- **Dijkstra 算法**:在所有边的权重均为非负数的图中,它是一种解决给定源顶点到其余各顶点的最短路径问题的贪心算法。 diff --git a/docs/chapter_greedy/max_product_cutting_problem.md b/docs/chapter_greedy/max_product_cutting_problem.md index 4569b67dc..4f1685264 100644 --- a/docs/chapter_greedy/max_product_cutting_problem.md +++ b/docs/chapter_greedy/max_product_cutting_problem.md @@ -51,7 +51,7 @@ $$ 1. 输入整数 $n$ ,从其不断地切分出因子 $3$ ,直至余数为 $0$、$1$、$2$ 。 2. 当余数为 $0$ 时,代表 $n$ 是 $3$ 的倍数,因此不做任何处理。 3. 当余数为 $2$ 时,不继续划分,保留。 -4. 当余数为 $1$ 时,由于 $2 \times 2 > 1 \times 3$ ,因此应将最后一个 $3$ 替换为 $2$ 。 +4. 当余数为 $1$ 时,由于 $2 \times 2 > 1 \times 3$ ,因此应将最后一个 $3$ 和余数 $1$ 替换为两个 $2$ 。 ### 代码实现 diff --git a/docs/chapter_hashing/hash_algorithm.md b/docs/chapter_hashing/hash_algorithm.md index 0ecf1f141..524c71f63 100644 --- a/docs/chapter_hashing/hash_algorithm.md +++ b/docs/chapter_hashing/hash_algorithm.md @@ -160,7 +160,7 @@ $$ size_t hashStr = hash()(str); // 字符串“Hello 算法”的哈希值为 15466937326284535026 - // 在 C++ 中,内置 std:hash() 仅提供基本数据类型的哈希值计算 + // 在 C++ 中,内置 std::hash() 仅提供基本数据类型的哈希值计算 // 数组、对象的哈希值计算需要自行实现 ``` diff --git a/docs/chapter_hashing/summary.md b/docs/chapter_hashing/summary.md index 0fa9e3845..d12593ae3 100644 --- a/docs/chapter_hashing/summary.md +++ b/docs/chapter_hashing/summary.md @@ -10,7 +10,7 @@ - 负载因子定义为哈希表中元素数量除以桶数量,反映了哈希冲突的严重程度,常用作触发哈希表扩容的条件。 - 链式地址通过将单个元素转化为链表,将所有冲突元素存储在同一个链表中。然而,链表过长会降低查询效率,可以通过进一步将链表转换为红黑树来提高效率。 - 开放寻址通过多次探测来处理哈希冲突。线性探测使用固定步长,缺点是不能删除元素,且容易产生聚集。多次哈希使用多个哈希函数进行探测,相较线性探测更不易产生聚集,但多个哈希函数增加了计算量。 -- 不同编程语言采取了不同的哈希表实现。例如,Java 的 `HashMap` 使用链式地址,而 Python 的 `Dict` 采用开放寻址。 +- 不同编程语言采取了不同的哈希表实现。例如,Java 的 `HashMap` 使用链式地址,而 Python 的 `dict` 采用开放寻址。 - 在哈希表中,我们希望哈希算法具有确定性、高效率和均匀分布的特点。在密码学中,哈希算法还应该具备抗碰撞性和雪崩效应。 - 哈希算法通常采用大质数作为模数,以最大化地保证哈希值均匀分布,减少哈希冲突。 - 常见的哈希算法包括 MD5、SHA-1、SHA-2 和 SHA-3 等。MD5 常用于校验文件完整性,SHA-2 常用于安全应用与协议。 diff --git a/docs/chapter_heap/build_heap.md b/docs/chapter_heap/build_heap.md index a2c3c505a..7bb7f86d8 100644 --- a/docs/chapter_heap/build_heap.md +++ b/docs/chapter_heap/build_heap.md @@ -31,7 +31,7 @@ 下面,我们来尝试推算第二种建堆方法的时间复杂度。 -- 假设完全二叉树的节点数量为 $n$ ,则叶节点数量为 $(n + 1) / 2$ ,其中 $/$ 为向下整除。因此需要堆化的节点数量为 $(n - 1) / 2$ 。 +- 假设完全二叉树的节点数量为 $n$ ,则叶节点数量为 $(n + 1) / 2$ ,其中 $/$ 为向下整除。因此需要堆化的节点数量为 $n / 2$ 。 - 在从顶至底堆化的过程中,每个节点最多堆化到叶节点,因此最大迭代次数为二叉树高度 $\log n$ 。 将上述两者相乘,可得到建堆过程的时间复杂度为 $O(n \log n)$ 。**但这个估算结果并不准确,因为我们没有考虑到二叉树底层节点数量远多于顶层节点的性质**。 diff --git a/docs/chapter_searching/binary_search_edge.md b/docs/chapter_searching/binary_search_edge.md index bccf0f50e..35fbe5e5a 100644 --- a/docs/chapter_searching/binary_search_edge.md +++ b/docs/chapter_searching/binary_search_edge.md @@ -41,7 +41,7 @@ ### 转化为查找元素 -我们知道,当数组不包含 `target` 时,最终 $i$ 和 $j$ 会分别指向首个大于、小于 `target` 的元素。 +我们知道,当数组不包含 `target` 时,最终 $i$ 和 $j$ 会分别指向首个大于 `target` 的元素和最右一个小于 `target` 的元素。 因此,如下图所示,我们可以构造一个数组中不存在的元素,用于查找左右边界。 diff --git a/docs/chapter_searching/binary_search_insertion.md b/docs/chapter_searching/binary_search_insertion.md index 1f2040e67..b4676c304 100644 --- a/docs/chapter_searching/binary_search_insertion.md +++ b/docs/chapter_searching/binary_search_insertion.md @@ -20,7 +20,7 @@ 进一步思考二分查找过程:当 `nums[m] < target` 时 $i$ 移动,这意味着指针 $i$ 在向大于等于 `target` 的元素靠近。同理,指针 $j$ 始终在向小于等于 `target` 的元素靠近。 -因此二分结束时一定有:$i$ 指向首个大于 `target` 的元素,$j$ 指向首个小于 `target` 的元素。**易得当数组不包含 `target` 时,插入索引为 $i$** 。代码如下所示: +因此二分结束时一定有:$i$ 指向首个大于 `target` 的元素,$j$ 指向最右一个小于 `target` 的元素。**易得当数组不包含 `target` 时,插入索引为 $i$** 。代码如下所示: ```src [file]{binary_search_insertion}-[class]{}-[func]{binary_search_insertion_simple} @@ -48,7 +48,7 @@ - 当 `nums[m] < target` 或 `nums[m] > target` 时,说明还没有找到 `target` ,因此采用普通二分查找的缩小区间操作,**从而使指针 $i$ 和 $j$ 向 `target` 靠近**。 - 当 `nums[m] == target` 时,说明小于 `target` 的元素在区间 $[i, m - 1]$ 中,因此采用 $j = m - 1$ 来缩小区间,**从而使指针 $j$ 向小于 `target` 的元素靠近**。 -循环完成后,$i$ 指向最左边的 `target` ,$j$ 指向首个小于 `target` 的元素,**因此索引 $i$ 就是插入点**。 +循环完成后,$i$ 指向最左边的 `target` ,$j$ 指向最右一个小于 `target` 的元素,**因此索引 $i$ 就是插入点**。 === "<1>" ![二分查找重复元素的插入点的步骤](binary_search_insertion.assets/binary_search_insertion_step1.png) diff --git a/docs/chapter_sorting/bucket_sort.md b/docs/chapter_sorting/bucket_sort.md index 84b7f08d0..71dd95e44 100644 --- a/docs/chapter_sorting/bucket_sort.md +++ b/docs/chapter_sorting/bucket_sort.md @@ -1,6 +1,6 @@ # 桶排序 -前述几种排序算法都属于“基于比较的排序算法”,它们通过比较元素间的大小来实现排序。此类排序算法的时间复杂度无法超越 $O(n \log n)$ 。接下来,我们将探讨几种“非比较排序算法”,它们的时间复杂度可以达到线性阶。 +前述几种排序算法都属于“基于比较的排序算法”,它们通过比较元素间的大小来实现排序。此类排序算法在最坏情况下的时间复杂度下界为 $\Omega(n \log n)$ 。接下来,我们将探讨几种“非比较排序算法”,它们的时间复杂度可以达到线性阶。 桶排序(bucket sort)是分治策略的一个典型应用。它通过设置一些具有大小顺序的桶,每个桶对应一个数据范围,将数据平均分配到各个桶中;然后,在每个桶内部分别执行排序;最终按照桶的顺序将所有数据合并。 diff --git a/docs/chapter_sorting/selection_sort.md b/docs/chapter_sorting/selection_sort.md index 80d3effc3..af2bd60b3 100644 --- a/docs/chapter_sorting/selection_sort.md +++ b/docs/chapter_sorting/selection_sort.md @@ -51,7 +51,7 @@ ## 算法特性 -- **时间复杂度为 $O(n^2)$、非自适应排序**:外循环共 $n - 1$ 轮,第一轮的未排序区间长度为 $n$ ,最后一轮的未排序区间长度为 $2$ ,即各轮外循环分别包含 $n$、$n - 1$、$\dots$、$3$、$2$ 轮内循环,求和为 $\frac{(n - 1)(n + 2)}{2}$ 。 +- **时间复杂度为 $O(n^2)$、非自适应排序**:外循环共 $n - 1$ 轮,第一轮内循环执行 $n - 1$ 次,最后一轮执行 $1$ 次,即各轮内循环分别执行 $n - 1$、$n - 2$、$\dots$、$2$、$1$ 次,求和为 $\frac{n(n - 1)}{2}$ 。 - **空间复杂度为 $O(1)$、原地排序**:指针 $i$ 和 $j$ 使用常数大小的额外空间。 - **非稳定排序**:如下图所示,元素 `nums[i]` 有可能被交换至与其相等的元素的右边,导致两者的相对顺序发生改变。 diff --git a/docs/chapter_sorting/sorting_algorithm.md b/docs/chapter_sorting/sorting_algorithm.md index ded41fd5a..48b55725b 100644 --- a/docs/chapter_sorting/sorting_algorithm.md +++ b/docs/chapter_sorting/sorting_algorithm.md @@ -37,7 +37,7 @@ **自适应性**:自适应排序能够利用输入数据已有的顺序信息来减少计算量,达到更优的时间效率。自适应排序算法的最佳时间复杂度通常优于平均时间复杂度。 -**是否基于比较**:基于比较的排序依赖比较运算符($<$、$=$、$>$)来判断元素的相对顺序,从而排序整个数组,理论最优时间复杂度为 $O(n \log n)$ 。而非比较排序不使用比较运算符,时间复杂度可达 $O(n)$ ,但其通用性相对较差。 +**是否基于比较**:基于比较的排序依赖比较运算符($<$、$=$、$>$)来判断元素的相对顺序,从而排序整个数组,其最坏时间复杂度的下界为 $\Omega(n \log n)$ 。而非比较排序不使用比较运算符,时间复杂度可达 $O(n)$ ,但其通用性相对较差。 ## 理想排序算法 diff --git a/docs/chapter_stack_and_queue/queue.md b/docs/chapter_stack_and_queue/queue.md index 0758fafc4..95d11dbf0 100755 --- a/docs/chapter_stack_and_queue/queue.md +++ b/docs/chapter_stack_and_queue/queue.md @@ -175,7 +175,7 @@ /* 元素出队 */ // 由于是数组,因此 removeFirst 的复杂度为 O(n) - let pool = queue.removeFirst() + let pop = queue.removeFirst() /* 获取队列的长度 */ let size = queue.count @@ -244,7 +244,7 @@ ```dart title="queue.dart" /* 初始化队列 */ - // 在 Dart 中,队列类 Qeque 是双向队列,也可作为队列使用 + // 在 Dart 中,队列类 Queue 是双向队列,也可作为队列使用 Queue queue = Queue(); /* 元素入队 */ diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index 31d3c6ca6..2bd07e637 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -205,9 +205,9 @@ AVL 树既是二叉搜索树,也是平衡二叉树,同时满足这两类二 ```kotlin title="" /* AVL 树节点类 */ class TreeNode(val _val: Int) { // 节点值 - val height: Int = 0 // 节点高度 - val left: TreeNode? = null // 左子节点 - val right: TreeNode? = null // 右子节点 + var height: Int = 0 // 节点高度 + var left: TreeNode? = null // 左子节点 + var right: TreeNode? = null // 右子节点 } ``` diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 72e9b3b80..549ebb043 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -181,8 +181,8 @@ ```kotlin title="" /* 二叉树节点类 */ class TreeNode(val _val: Int) { // 节点值 - val left: TreeNode? = null // 左子节点引用 - val right: TreeNode? = null // 右子节点引用 + var left: TreeNode? = null // 左子节点引用 + var right: TreeNode? = null // 右子节点引用 } ``` diff --git a/en/codes/c/chapter_searching/binary_search_edge.c b/en/codes/c/chapter_searching/binary_search_edge.c index d9bdc6858..5d295bd55 100644 --- a/en/codes/c/chapter_searching/binary_search_edge.c +++ b/en/codes/c/chapter_searching/binary_search_edge.c @@ -14,7 +14,7 @@ int binarySearchInsertion(int *nums, int numSize, int target) { if (nums[m] < target) { i = m + 1; // target is in the interval [m+1, j] } else { - j = m - 1; // The first element less than target is in the interval [i, m-1] + j = m - 1; // The rightmost element less than target is in the interval [i, m-1] } } // Return insertion point i @@ -64,4 +64,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/en/codes/c/chapter_searching/binary_search_insertion.c b/en/codes/c/chapter_searching/binary_search_insertion.c index ec76a1571..c7b6401fc 100644 --- a/en/codes/c/chapter_searching/binary_search_insertion.c +++ b/en/codes/c/chapter_searching/binary_search_insertion.c @@ -33,7 +33,7 @@ int binarySearchInsertion(int *nums, int numSize, int target) { } else if (nums[m] > target) { j = m - 1; // target is in the interval [i, m-1] } else { - j = m - 1; // The first element less than target is in the interval [i, m-1] + j = m - 1; // The rightmost element less than target is in the interval [i, m-1] } } // Return insertion point i diff --git a/en/codes/cpp/chapter_searching/binary_search_edge.cpp b/en/codes/cpp/chapter_searching/binary_search_edge.cpp index a2e794d1c..83dd3464d 100644 --- a/en/codes/cpp/chapter_searching/binary_search_edge.cpp +++ b/en/codes/cpp/chapter_searching/binary_search_edge.cpp @@ -14,7 +14,7 @@ int binarySearchInsertion(const vector &nums, int target) { if (nums[m] < target) { i = m + 1; // target is in the interval [m+1, j] } else { - j = m - 1; // The first element less than target is in the interval [i, m-1] + j = m - 1; // The rightmost element less than target is in the interval [i, m-1] } } // Return insertion point i diff --git a/en/codes/cpp/chapter_searching/binary_search_insertion.cpp b/en/codes/cpp/chapter_searching/binary_search_insertion.cpp index 2278b2062..68ef51257 100644 --- a/en/codes/cpp/chapter_searching/binary_search_insertion.cpp +++ b/en/codes/cpp/chapter_searching/binary_search_insertion.cpp @@ -33,7 +33,7 @@ int binarySearchInsertion(vector &nums, int target) { } else if (nums[m] > target) { j = m - 1; // target is in the interval [i, m-1] } else { - j = m - 1; // The first element less than target is in the interval [i, m-1] + j = m - 1; // The rightmost element less than target is in the interval [i, m-1] } } // Return insertion point i diff --git a/en/codes/csharp/chapter_searching/binary_search_insertion.cs b/en/codes/csharp/chapter_searching/binary_search_insertion.cs index 3c5519550..3a06f2ec9 100644 --- a/en/codes/csharp/chapter_searching/binary_search_insertion.cs +++ b/en/codes/csharp/chapter_searching/binary_search_insertion.cs @@ -34,7 +34,7 @@ public class binary_search_insertion { } else if (nums[m] > target) { j = m - 1; // target is in the interval [i, m-1] } else { - j = m - 1; // The first element less than target is in the interval [i, m-1] + j = m - 1; // The rightmost element less than target is in the interval [i, m-1] } } // Return insertion point i diff --git a/en/codes/dart/chapter_searching/binary_search_insertion.dart b/en/codes/dart/chapter_searching/binary_search_insertion.dart index f40947a22..13f9fdb5e 100644 --- a/en/codes/dart/chapter_searching/binary_search_insertion.dart +++ b/en/codes/dart/chapter_searching/binary_search_insertion.dart @@ -31,7 +31,7 @@ int binarySearchInsertion(List nums, int target) { } else if (nums[m] > target) { j = m - 1; // target is in the interval [i, m-1] } else { - j = m - 1; // The first element less than target is in the interval [i, m-1] + j = m - 1; // The rightmost element less than target is in the interval [i, m-1] } } // Return insertion point i diff --git a/en/codes/go/chapter_searching/binary_search_insertion.go b/en/codes/go/chapter_searching/binary_search_insertion.go index d01fc1dd7..22e139079 100644 --- a/en/codes/go/chapter_searching/binary_search_insertion.go +++ b/en/codes/go/chapter_searching/binary_search_insertion.go @@ -40,7 +40,7 @@ func binarySearchInsertion(nums []int, target int) int { // target is in the interval [i, m-1] j = m - 1 } else { - // The first element less than target is in the interval [i, m-1] + // The rightmost element less than target is in the interval [i, m-1] j = m - 1 } } diff --git a/en/codes/java/chapter_searching/binary_search_insertion.java b/en/codes/java/chapter_searching/binary_search_insertion.java index 0e7bb97fd..4b20deaf6 100644 --- a/en/codes/java/chapter_searching/binary_search_insertion.java +++ b/en/codes/java/chapter_searching/binary_search_insertion.java @@ -34,7 +34,7 @@ class binary_search_insertion { } else if (nums[m] > target) { j = m - 1; // target is in the interval [i, m-1] } else { - j = m - 1; // The first element less than target is in the interval [i, m-1] + j = m - 1; // The rightmost element less than target is in the interval [i, m-1] } } // Return insertion point i diff --git a/en/codes/javascript/chapter_searching/binary_search_insertion.js b/en/codes/javascript/chapter_searching/binary_search_insertion.js index a43d3a897..17a149ae2 100644 --- a/en/codes/javascript/chapter_searching/binary_search_insertion.js +++ b/en/codes/javascript/chapter_searching/binary_search_insertion.js @@ -33,7 +33,7 @@ function binarySearchInsertion(nums, target) { } else if (nums[m] > target) { j = m - 1; // target is in the interval [i, m-1] } else { - j = m - 1; // The first element less than target is in the interval [i, m-1] + j = m - 1; // The rightmost element less than target is in the interval [i, m-1] } } // Return insertion point i @@ -47,7 +47,7 @@ console.log('\nArray nums = ' + nums); // Binary search for insertion point for (const target of [6, 9]) { const index = binarySearchInsertionSimple(nums, target); - console.log('Element ' + target + ''s insertion point index is ' + index); + console.log('Element ' + target + "'s insertion point index is " + index); } // Array with duplicate elements @@ -56,7 +56,7 @@ console.log('\nArray nums = ' + nums); // Binary search for insertion point for (const target of [2, 6, 20]) { const index = binarySearchInsertion(nums, target); - console.log('Element ' + target + ''s insertion point index is ' + index); + console.log('Element ' + target + "'s insertion point index is " + index); } module.exports = { diff --git a/en/codes/kotlin/chapter_searching/binary_search_insertion.kt b/en/codes/kotlin/chapter_searching/binary_search_insertion.kt index 5093a80e7..2b9cf71d7 100644 --- a/en/codes/kotlin/chapter_searching/binary_search_insertion.kt +++ b/en/codes/kotlin/chapter_searching/binary_search_insertion.kt @@ -35,7 +35,7 @@ fun binarySearchInsertion(nums: IntArray, target: Int): Int { } else if (nums[m] > target) { j = m - 1 // target is in the interval [i, m-1] } else { - j = m - 1 // The first element less than target is in the interval [i, m-1] + j = m - 1 // The rightmost element less than target is in the interval [i, m-1] } } // Return insertion point i @@ -62,4 +62,4 @@ fun main() { val index = binarySearchInsertion(nums, target) println("Insertion point index for element $target is $index") } -} \ No newline at end of file +} diff --git a/en/codes/python/chapter_searching/binary_search_insertion.py b/en/codes/python/chapter_searching/binary_search_insertion.py index 8d1271105..95c909574 100644 --- a/en/codes/python/chapter_searching/binary_search_insertion.py +++ b/en/codes/python/chapter_searching/binary_search_insertion.py @@ -30,7 +30,7 @@ def binary_search_insertion(nums: list[int], target: int) -> int: elif nums[m] > target: j = m - 1 # target is in the interval [i, m-1] else: - j = m - 1 # The first element less than target is in the interval [i, m-1] + j = m - 1 # The rightmost element less than target is in the interval [i, m-1] # Return insertion point i return i diff --git a/en/codes/ruby/chapter_backtracking/permutations_ii.rb b/en/codes/ruby/chapter_backtracking/permutations_ii.rb index 70800f7a6..288f74474 100644 --- a/en/codes/ruby/chapter_backtracking/permutations_ii.rb +++ b/en/codes/ruby/chapter_backtracking/permutations_ii.rb @@ -4,6 +4,8 @@ Created Time: 2024-05-22 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end +require 'set' + ### Backtracking: permutations II ### def backtrack(state, choices, selected, res) # When the state length equals the number of elements, record the solution diff --git a/en/codes/ruby/chapter_searching/binary_search_insertion.rb b/en/codes/ruby/chapter_searching/binary_search_insertion.rb index bcf47a4d4..33f13a3c5 100644 --- a/en/codes/ruby/chapter_searching/binary_search_insertion.rb +++ b/en/codes/ruby/chapter_searching/binary_search_insertion.rb @@ -39,7 +39,7 @@ def binary_search_insertion(nums, target) elsif nums[m] > target j = m - 1 # target is in the interval [i, m-1] else - j = m - 1 # The first element less than target is in the interval [i, m-1] + j = m - 1 # The rightmost element less than target is in the interval [i, m-1] end end diff --git a/en/codes/rust/chapter_searching/binary_search_insertion.rs b/en/codes/rust/chapter_searching/binary_search_insertion.rs index 53ce9ea40..4063de451 100644 --- a/en/codes/rust/chapter_searching/binary_search_insertion.rs +++ b/en/codes/rust/chapter_searching/binary_search_insertion.rs @@ -32,7 +32,7 @@ pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 { } else if nums[m as usize] > target { j = m - 1; // target is in the interval [i, m-1] } else { - j = m - 1; // The first element less than target is in the interval [i, m-1] + j = m - 1; // The rightmost element less than target is in the interval [i, m-1] } } // Return insertion point i diff --git a/en/codes/swift/chapter_searching/binary_search_insertion.swift b/en/codes/swift/chapter_searching/binary_search_insertion.swift index 9d6b0f7ff..52e10a5fc 100644 --- a/en/codes/swift/chapter_searching/binary_search_insertion.swift +++ b/en/codes/swift/chapter_searching/binary_search_insertion.swift @@ -35,7 +35,7 @@ public func binarySearchInsertion(nums: [Int], target: Int) -> Int { } else if nums[m] > target { j = m - 1 // target is in the interval [i, m-1] } else { - j = m - 1 // The first element less than target is in the interval [i, m-1] + j = m - 1 // The rightmost element less than target is in the interval [i, m-1] } } // Return insertion point i diff --git a/en/codes/swift/chapter_searching/binary_search_insertion_target.swift b/en/codes/swift/chapter_searching/binary_search_insertion_target.swift index 9d6b0f7ff..52e10a5fc 100644 --- a/en/codes/swift/chapter_searching/binary_search_insertion_target.swift +++ b/en/codes/swift/chapter_searching/binary_search_insertion_target.swift @@ -35,7 +35,7 @@ public func binarySearchInsertion(nums: [Int], target: Int) -> Int { } else if nums[m] > target { j = m - 1 // target is in the interval [i, m-1] } else { - j = m - 1 // The first element less than target is in the interval [i, m-1] + j = m - 1 // The rightmost element less than target is in the interval [i, m-1] } } // Return insertion point i diff --git a/en/codes/swift/chapter_stack_and_queue/queue.swift b/en/codes/swift/chapter_stack_and_queue/queue.swift index 3c1270346..c9bea78d3 100644 --- a/en/codes/swift/chapter_stack_and_queue/queue.swift +++ b/en/codes/swift/chapter_stack_and_queue/queue.swift @@ -26,8 +26,8 @@ enum Queue { /* Element dequeue */ // When simulating with Array, pop complexity is O(n) - let pool = queue.removeFirst() - print("Dequeue element pop = \(pool), after dequeue queue = \(queue)") + let pop = queue.removeFirst() + print("Dequeue element pop = \(pop), after dequeue queue = \(queue)") /* Get the length of the queue */ let size = queue.count diff --git a/en/codes/typescript/chapter_searching/binary_search_insertion.ts b/en/codes/typescript/chapter_searching/binary_search_insertion.ts index d32429ac4..14a416ff9 100644 --- a/en/codes/typescript/chapter_searching/binary_search_insertion.ts +++ b/en/codes/typescript/chapter_searching/binary_search_insertion.ts @@ -36,7 +36,7 @@ function binarySearchInsertion(nums: Array, target: number): number { } else if (nums[m] > target) { j = m - 1; // target is in the interval [i, m-1] } else { - j = m - 1; // The first element less than target is in the interval [i, m-1] + j = m - 1; // The rightmost element less than target is in the interval [i, m-1] } } // Return insertion point i @@ -50,7 +50,7 @@ console.log('\nArray nums = ' + nums); // Binary search for insertion point for (const target of [6, 9]) { const index = binarySearchInsertionSimple(nums, target); - console.log('Element ' + target + ''s insertion point index is ' + index); + console.log('Element ' + target + "'s insertion point index is " + index); } // Array with duplicate elements @@ -59,7 +59,7 @@ console.log('\nArray nums = ' + nums); // Binary search for insertion point for (const target of [2, 6, 20]) { const index = binarySearchInsertion(nums, target); - console.log('Element ' + target + ''s insertion point index is ' + index); + console.log('Element ' + target + "'s insertion point index is " + index); } export { binarySearchInsertion }; diff --git a/en/docs/chapter_appendix/terminology.md b/en/docs/chapter_appendix/terminology.md index b68472e73..85b52e66b 100644 --- a/en/docs/chapter_appendix/terminology.md +++ b/en/docs/chapter_appendix/terminology.md @@ -80,7 +80,9 @@ The following table lists important terms that appear in this book. | level-order traversal | | breadth-first traversal | | depth-first traversal | -| binary search tree | +| pre-order traversal | +| in-order traversal | +| post-order traversal | | balanced binary search tree | | balance factor | | heap | diff --git a/en/docs/chapter_array_and_linkedlist/linked_list.md b/en/docs/chapter_array_and_linkedlist/linked_list.md index 6c8faa52d..1288329fb 100755 --- a/en/docs/chapter_array_and_linkedlist/linked_list.md +++ b/en/docs/chapter_array_and_linkedlist/linked_list.md @@ -166,7 +166,7 @@ As shown in the following code, a linked list node `ListNode` contains not only // Constructor class ListNode(x: Int) { val _val: Int = x // Node value - val next: ListNode? = null // Reference to the next node + var next: ListNode? = null // Reference to the next node } ``` @@ -658,8 +658,8 @@ As shown in the figure below, there are three common types of linked lists: // Constructor class ListNode(x: Int) { val _val: Int = x // Node value - val next: ListNode? = null // Reference to the successor node - val prev: ListNode? = null // Reference to the predecessor node + var next: ListNode? = null // Reference to the successor node + var prev: ListNode? = null // Reference to the predecessor node } ``` diff --git a/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md b/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md index df70242c9..89beab368 100644 --- a/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md +++ b/en/docs/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -21,12 +21,12 @@ Based on the above analysis, this problem can be solved using divide and conquer According to the definition, both `preorder` and `inorder` can be divided into three parts. - Preorder traversal: `[ Root Node | Left Subtree | Right Subtree ]`, for example, the tree in the figure above corresponds to `[ 3 | 9 | 2 1 7 ]`. -- Inorder traversal: `[ Left Subtree | Root Node | Right Subtree ]`, for example, the tree in the figure above corresponds to `[ 9 | 3 | 1 2 7 ]`. +- Inorder traversal: `[ Left Subtree | Root Node | Right Subtree ]`, for example, the tree in the figure above corresponds to `[ 9 | 3 | 1 2 7 ]`. Using the data from the figure above as an example, we can obtain the division results through the steps shown in the figure below. 1. The first element 3 in the preorder traversal is the value of the root node. -2. Find the index of root node 3 in `inorder`, and use this index to divide `inorder` into `[ 9 | 3 | 1 2 7 ]`. +2. Find the index of root node 3 in `inorder`, and use this index to divide `inorder` into `[ 9 | 3 | 1 2 7 ]`. 3. Based on the division result of `inorder`, it is easy to determine that the left and right subtrees have 1 and 3 nodes respectively, allowing us to divide `preorder` into `[ 3 | 9 | 2 1 7 ]`. ![Dividing subtrees in preorder and inorder traversals](build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png) diff --git a/en/docs/chapter_dynamic_programming/edit_distance_problem.md b/en/docs/chapter_dynamic_programming/edit_distance_problem.md index 3e16965fc..bfbce88cb 100644 --- a/en/docs/chapter_dynamic_programming/edit_distance_problem.md +++ b/en/docs/chapter_dynamic_programming/edit_distance_problem.md @@ -35,7 +35,7 @@ In other words, each round of decision (edit operation) we make on string $s$ wi State $[i, j]$ corresponds to the subproblem: **the minimum number of edits required to change the first $i$ characters of $s$ into the first $j$ characters of $t$**. -From this, we obtain a two-dimensional $dp$ table of size $(i+1) \times (j+1)$. +From this, we obtain a two-dimensional $dp$ table of size $(n+1) \times (m+1)$. **Step 2: Identify the optimal substructure, and then derive the state transition equation** diff --git a/en/docs/chapter_dynamic_programming/knapsack_problem.md b/en/docs/chapter_dynamic_programming/knapsack_problem.md index d8a26a873..6599d02ed 100644 --- a/en/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/en/docs/chapter_dynamic_programming/knapsack_problem.md @@ -134,7 +134,7 @@ As shown in the figure below, both time complexity and space complexity are dete ### Space Optimization -Since each state is only related to the state in the row above it, we can use two arrays rolling forward to reduce the space complexity from $O(n^2)$ to $O(n)$. +Since each state is only related to the state in the row above it, we can use two arrays rolling forward to reduce the space complexity from $O(n \times cap)$ to $O(cap)$. Further thinking, can we achieve space optimization using just one array? Observing, we can see that each state is transferred from the cell directly above or the cell in the upper-left. If there is only one array, when we start traversing row $i$, that array still stores the state of row $i-1$. diff --git a/en/docs/chapter_dynamic_programming/summary.md b/en/docs/chapter_dynamic_programming/summary.md index f7b1b3968..3fc0f18c9 100644 --- a/en/docs/chapter_dynamic_programming/summary.md +++ b/en/docs/chapter_dynamic_programming/summary.md @@ -21,5 +21,5 @@ **Edit distance problem** - Edit distance (Levenshtein distance) is used to measure the similarity between two strings, defined as the minimum number of edit steps from one string to another, with edit operations including insert, delete, and replace. -- The state definition for the edit distance problem is the minimum number of edit steps required to change the first $i$ characters of $s$ into the first $j$ characters of $t$. When $s[i] \ne t[j]$, there are three decisions: insert, delete, replace, each with corresponding remaining subproblems. From this, the optimal substructure can be identified and the state transition equation constructed. When $s[i] = t[j]$, no edit is required for the current character. +- The state definition for the edit distance problem is the minimum number of edit steps required to change the first $i$ characters of $s$ into the first $j$ characters of $t$. When $s[i-1] \ne t[j-1]$, there are three decisions: insert, delete, replace, each with corresponding remaining subproblems. From this, the optimal substructure can be identified and the state transition equation constructed. When $s[i-1] = t[j-1]$, no edit is required for the current character. - In edit distance, the state depends on the state directly above, directly to the left, and to the upper-left, so after space optimization, neither forward nor reverse traversal can correctly perform state transitions. For this reason, we use a variable to temporarily store the upper-left state, thus transforming to a situation equivalent to the unbounded knapsack problem, allowing for forward traversal after space optimization. diff --git a/en/docs/chapter_greedy/greedy_algorithm.md b/en/docs/chapter_greedy/greedy_algorithm.md index e8c9d04ba..f3b1711e0 100644 --- a/en/docs/chapter_greedy/greedy_algorithm.md +++ b/en/docs/chapter_greedy/greedy_algorithm.md @@ -91,4 +91,4 @@ Greedy algorithms are often applied to optimization problems that satisfy greedy - **Fractional knapsack problem**: Given a set of items and a carrying capacity, your goal is to select a set of items such that the total weight does not exceed the carrying capacity and the total value is maximized. If you always choose the item with the highest value-to-weight ratio (value / weight), then the greedy algorithm can obtain the optimal solution in some cases. - **Stock trading problem**: Given a set of historical stock prices, you can make multiple trades, but if you already hold stocks, you cannot buy again before selling, and the goal is to obtain the maximum profit. - **Huffman coding**: Huffman coding is a greedy algorithm used for lossless data compression. By constructing a Huffman tree and always merging the two nodes with the lowest frequency, the resulting Huffman tree has the minimum weighted path length (encoding length). -- **Dijkstra's algorithm**: It is a greedy algorithm for solving the shortest path problem from a given source vertex to all other vertices. +- **Dijkstra's algorithm**: For graphs with non-negative edge weights, it is a greedy algorithm for solving the shortest path problem from a given source vertex to all other vertices. diff --git a/en/docs/chapter_heap/build_heap.md b/en/docs/chapter_heap/build_heap.md index cd1313fe9..8cc206d31 100644 --- a/en/docs/chapter_heap/build_heap.md +++ b/en/docs/chapter_heap/build_heap.md @@ -31,7 +31,7 @@ It's worth noting that **since leaf nodes have no children, they are naturally v Next, let's attempt to derive the time complexity of this second heap construction method. -- Assuming the complete binary tree has $n$ nodes, then the number of leaf nodes is $(n + 1) / 2$, where $/$ is floor division. Therefore, the number of nodes that need heapification is $(n - 1) / 2$. +- Assuming the complete binary tree has $n$ nodes, then the number of leaf nodes is $(n + 1) / 2$, where $/$ is floor division. Therefore, the number of nodes that need heapification is $n / 2$. - In the top-to-bottom heapify process, each node can sink at most to a leaf node, so the maximum number of iterations is the height of the binary tree, $\log n$. Multiplying these two together, we get a time complexity of $O(n \log n)$ for the heap construction process. **However, this estimate is not accurate because it doesn't account for the property that binary trees have far more nodes at lower levels than at upper levels**. diff --git a/en/docs/chapter_searching/binary_search_edge.md b/en/docs/chapter_searching/binary_search_edge.md index f3ef0d3dc..8dc32d019 100644 --- a/en/docs/chapter_searching/binary_search_edge.md +++ b/en/docs/chapter_searching/binary_search_edge.md @@ -41,7 +41,7 @@ Note that the returned insertion point is $i$, so we need to subtract $1$ from i ### Converting to Element Search -We know that when the array does not contain `target`, $i$ and $j$ will eventually point to the first elements greater than and less than `target`, respectively. +We know that when the array does not contain `target`, $i$ and $j$ will eventually point to the first element greater than `target` and the rightmost element less than `target`, respectively. Therefore, as shown in the figure below, we can construct an element that does not exist in the array to find the left and right boundaries. diff --git a/en/docs/chapter_searching/binary_search_insertion.md b/en/docs/chapter_searching/binary_search_insertion.md index 07262dc8d..384f0fd6b 100644 --- a/en/docs/chapter_searching/binary_search_insertion.md +++ b/en/docs/chapter_searching/binary_search_insertion.md @@ -20,7 +20,7 @@ The problem requires inserting `target` to the left of equal elements, which mea To analyze this further, consider the binary search process: when `nums[m] < target`, $i$ moves, meaning that pointer $i$ is approaching elements greater than or equal to `target`. Similarly, pointer $j$ is always approaching elements less than or equal to `target`. -Therefore, when the binary search ends, $i$ must point to the first element greater than `target`, and $j$ must point to the first element less than `target`. **It follows that when the array does not contain `target`, the insertion index is $i$**. The code is shown below: +Therefore, when the binary search ends, $i$ must point to the first element greater than `target`, and $j$ must point to the rightmost element less than `target`. **It follows that when the array does not contain `target`, the insertion index is $i$**. The code is shown below: ```src [file]{binary_search_insertion}-[class]{}-[func]{binary_search_insertion_simple} @@ -48,7 +48,7 @@ Now consider extending the binary search code. As shown in the figure below, the - When `nums[m] < target` or `nums[m] > target`, it means `target` has not been found yet, so use the standard interval-shrinking operation of binary search to **move pointers $i$ and $j$ closer to `target`**. - When `nums[m] == target`, it means elements less than `target` are in the interval $[i, m - 1]$, so use $j = m - 1$ to shrink the interval, thereby **moving pointer $j$ closer to elements less than `target`**. -After the loop completes, $i$ points to the leftmost `target`, and $j$ points to the first element less than `target`, **so index $i$ is the insertion point**. +After the loop completes, $i$ points to the leftmost `target`, and $j$ points to the rightmost element less than `target`, **so index $i$ is the insertion point**. === "<1>" ![Steps for binary search insertion point of duplicate elements](binary_search_insertion.assets/binary_search_insertion_step1.png) diff --git a/en/docs/chapter_sorting/bucket_sort.md b/en/docs/chapter_sorting/bucket_sort.md index af7b3e694..625cfb85e 100644 --- a/en/docs/chapter_sorting/bucket_sort.md +++ b/en/docs/chapter_sorting/bucket_sort.md @@ -1,6 +1,6 @@ # Bucket Sort -The sorting algorithms discussed earlier are all comparison-based sorting algorithms, which sort by comparing the relative order of elements. The time complexity of such algorithms cannot beat $O(n \log n)$. Next, we will explore several non-comparison sorting algorithms, whose time complexity can be linear. +The sorting algorithms discussed earlier are all comparison-based sorting algorithms, which sort by comparing the relative order of elements. The worst-case time complexity of such algorithms has a lower bound of $\Omega(n \log n)$. Next, we will explore several non-comparison sorting algorithms, whose time complexity can be linear. Bucket sort is a typical application of the divide-and-conquer strategy. It works by creating a sequence of ordered buckets, each corresponding to a data range, and distributing the data evenly among them. The elements within each bucket are then sorted separately. Finally, all buckets are merged in order. diff --git a/en/docs/chapter_sorting/selection_sort.md b/en/docs/chapter_sorting/selection_sort.md index c1605ac55..d37644bcd 100644 --- a/en/docs/chapter_sorting/selection_sort.md +++ b/en/docs/chapter_sorting/selection_sort.md @@ -51,7 +51,7 @@ In the code, we use $k$ to track the smallest element within the unsorted interv ## Algorithm Characteristics -- **Time complexity $O(n^2)$, non-adaptive sorting**: The outer loop has $n - 1$ rounds in total. The length of the unsorted interval in the first round is $n$, and the length of the unsorted interval in the last round is $2$. That is, the rounds of the outer loop contain inner loops with $n$, $n - 1$, $\dots$, $3$, and $2$ iterations, summing to $\frac{(n - 1)(n + 2)}{2}$. +- **Time complexity $O(n^2)$, non-adaptive sorting**: The outer loop has $n - 1$ rounds in total. The inner loop runs $n - 1$ times in the first round and $1$ time in the last round. Thus, it runs $n - 1$, $n - 2$, $\dots$, $2$, and $1$ times across the rounds, summing to $\frac{n(n - 1)}{2}$. - **Space complexity $O(1)$, in-place sorting**: Pointers $i$ and $j$ use a constant amount of extra space. - **Unstable sorting**: As shown in the figure below, element `nums[i]` may be swapped to the right of an element equal to it, causing a change in their relative order. diff --git a/en/docs/chapter_sorting/sorting_algorithm.md b/en/docs/chapter_sorting/sorting_algorithm.md index 1ee758867..df15d1295 100644 --- a/en/docs/chapter_sorting/sorting_algorithm.md +++ b/en/docs/chapter_sorting/sorting_algorithm.md @@ -37,7 +37,7 @@ Stable sorting is a necessary condition for multi-level sorting scenarios. Suppo **Adaptability**: Adaptive sorting can utilize the existing order information in the input data to reduce the amount of computation, achieving better time efficiency. The best-case time complexity of adaptive sorting algorithms is typically better than the average time complexity. -**Comparison-based or non-comparison**: Comparison-based sorting relies on comparison operators ($<$, $=$, $>$) to determine the relative order of elements, thereby sorting the entire array, with a theoretical optimal time complexity of $O(n \log n)$. Non-comparison sorting does not use comparison operators and can achieve a time complexity of $O(n)$, but its versatility is relatively limited. +**Comparison-based or non-comparison**: Comparison-based sorting relies on comparison operators ($<$, $=$, $>$) to determine the relative order of elements, thereby sorting the entire array. Its worst-case time complexity has a lower bound of $\Omega(n \log n)$. Non-comparison sorting does not use comparison operators and can achieve a time complexity of $O(n)$, but its versatility is relatively limited. ## Ideal Sorting Algorithm diff --git a/en/docs/chapter_stack_and_queue/queue.md b/en/docs/chapter_stack_and_queue/queue.md index cab140396..adcc4ca71 100755 --- a/en/docs/chapter_stack_and_queue/queue.md +++ b/en/docs/chapter_stack_and_queue/queue.md @@ -175,7 +175,7 @@ We can directly use the queue classes provided by the programming language: /* Dequeue element */ // Since it's an array, removeFirst has O(n) complexity - let pool = queue.removeFirst() + let pop = queue.removeFirst() /* Get queue length */ let size = queue.count diff --git a/en/docs/chapter_tree/avl_tree.md b/en/docs/chapter_tree/avl_tree.md index ecabb9489..620628054 100644 --- a/en/docs/chapter_tree/avl_tree.md +++ b/en/docs/chapter_tree/avl_tree.md @@ -205,9 +205,9 @@ Since the operations related to AVL trees require obtaining node heights, we nee ```kotlin title="" /* AVL tree node */ class TreeNode(val _val: Int) { // Node value - val height: Int = 0 // Node height - val left: TreeNode? = null // Left child - val right: TreeNode? = null // Right child + var height: Int = 0 // Node height + var left: TreeNode? = null // Left child + var right: TreeNode? = null // Right child } ``` diff --git a/en/docs/chapter_tree/binary_tree.md b/en/docs/chapter_tree/binary_tree.md index 03252e987..c883777be 100644 --- a/en/docs/chapter_tree/binary_tree.md +++ b/en/docs/chapter_tree/binary_tree.md @@ -181,8 +181,8 @@ A binary tree is a non-linear data structure that models the hierarchical ```kotlin title="" /* Binary tree node */ class TreeNode(val _val: Int) { // Node value - val left: TreeNode? = null // Reference to left child node - val right: TreeNode? = null // Reference to right child node + var left: TreeNode? = null // Reference to left child node + var right: TreeNode? = null // Reference to right child node } ``` diff --git a/ja/codes/c/chapter_searching/binary_search_edge.c b/ja/codes/c/chapter_searching/binary_search_edge.c index 485563631..66b93e427 100644 --- a/ja/codes/c/chapter_searching/binary_search_edge.c +++ b/ja/codes/c/chapter_searching/binary_search_edge.c @@ -14,7 +14,7 @@ int binarySearchInsertion(int *nums, int numSize, int target) { if (nums[m] < target) { i = m + 1; // target は区間 [m+1, j] にある } else { - j = m - 1; // target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1; // target より小さい最も右の要素は区間 [i, m-1] にある } } // 挿入位置 i を返す @@ -64,4 +64,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/ja/codes/c/chapter_searching/binary_search_insertion.c b/ja/codes/c/chapter_searching/binary_search_insertion.c index a980ee5a0..901cff6da 100644 --- a/ja/codes/c/chapter_searching/binary_search_insertion.c +++ b/ja/codes/c/chapter_searching/binary_search_insertion.c @@ -33,7 +33,7 @@ int binarySearchInsertion(int *nums, int numSize, int target) { } else if (nums[m] > target) { j = m - 1; // target は区間 [i, m-1] にある } else { - j = m - 1; // target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1; // target より小さい最も右の要素は区間 [i, m-1] にある } } // 挿入位置 i を返す diff --git a/ja/codes/c/chapter_stack_and_queue/array_queue.c b/ja/codes/c/chapter_stack_and_queue/array_queue.c index f0b47f839..873f69afa 100644 --- a/ja/codes/c/chapter_stack_and_queue/array_queue.c +++ b/ja/codes/c/chapter_stack_and_queue/array_queue.c @@ -131,4 +131,4 @@ int main() { delArrayQueue(queue); return 0; -} +} \ No newline at end of file diff --git a/ja/codes/cpp/chapter_searching/binary_search_edge.cpp b/ja/codes/cpp/chapter_searching/binary_search_edge.cpp index ba0213efc..720d5a618 100644 --- a/ja/codes/cpp/chapter_searching/binary_search_edge.cpp +++ b/ja/codes/cpp/chapter_searching/binary_search_edge.cpp @@ -14,7 +14,7 @@ int binarySearchInsertion(const vector &nums, int target) { if (nums[m] < target) { i = m + 1; // target は区間 [m+1, j] にある } else { - j = m - 1; // target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1; // target より小さい最も右の要素は区間 [i, m-1] にある } } // 挿入位置 i を返す diff --git a/ja/codes/cpp/chapter_searching/binary_search_insertion.cpp b/ja/codes/cpp/chapter_searching/binary_search_insertion.cpp index f076f80c3..578fa657b 100644 --- a/ja/codes/cpp/chapter_searching/binary_search_insertion.cpp +++ b/ja/codes/cpp/chapter_searching/binary_search_insertion.cpp @@ -33,7 +33,7 @@ int binarySearchInsertion(vector &nums, int target) { } else if (nums[m] > target) { j = m - 1; // target は区間 [i, m-1] にある } else { - j = m - 1; // target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1; // target より小さい最も右の要素は区間 [i, m-1] にある } } // 挿入位置 i を返す diff --git a/ja/codes/csharp/chapter_searching/binary_search_insertion.cs b/ja/codes/csharp/chapter_searching/binary_search_insertion.cs index 78b3e9e27..ceef80620 100644 --- a/ja/codes/csharp/chapter_searching/binary_search_insertion.cs +++ b/ja/codes/csharp/chapter_searching/binary_search_insertion.cs @@ -34,7 +34,7 @@ public class binary_search_insertion { } else if (nums[m] > target) { j = m - 1; // target は区間 [i, m-1] にある } else { - j = m - 1; // target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1; // target より小さい最も右の要素は区間 [i, m-1] にある } } // 挿入位置 i を返す diff --git a/ja/codes/dart/chapter_searching/binary_search_insertion.dart b/ja/codes/dart/chapter_searching/binary_search_insertion.dart index 35c4ca836..9b1d5f891 100644 --- a/ja/codes/dart/chapter_searching/binary_search_insertion.dart +++ b/ja/codes/dart/chapter_searching/binary_search_insertion.dart @@ -31,7 +31,7 @@ int binarySearchInsertion(List nums, int target) { } else if (nums[m] > target) { j = m - 1; // target は区間 [i, m-1] にある } else { - j = m - 1; // target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1; // target より小さい最も右の要素は区間 [i, m-1] にある } } // 挿入位置 i を返す diff --git a/ja/codes/go/chapter_searching/binary_search_insertion.go b/ja/codes/go/chapter_searching/binary_search_insertion.go index 0523347df..cd562bc79 100644 --- a/ja/codes/go/chapter_searching/binary_search_insertion.go +++ b/ja/codes/go/chapter_searching/binary_search_insertion.go @@ -40,7 +40,7 @@ func binarySearchInsertion(nums []int, target int) int { // target は区間 [i, m-1] にある j = m - 1 } else { - // target より小さい最初の要素は区間 [i, m-1] にある + // target より小さい最も右の要素は区間 [i, m-1] にある j = m - 1 } } diff --git a/ja/codes/java/chapter_searching/binary_search_insertion.java b/ja/codes/java/chapter_searching/binary_search_insertion.java index 3f5a00da9..97ba5e76a 100644 --- a/ja/codes/java/chapter_searching/binary_search_insertion.java +++ b/ja/codes/java/chapter_searching/binary_search_insertion.java @@ -34,7 +34,7 @@ class binary_search_insertion { } else if (nums[m] > target) { j = m - 1; // target は区間 [i, m-1] にある } else { - j = m - 1; // target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1; // target より小さい最も右の要素は区間 [i, m-1] にある } } // 挿入位置 i を返す diff --git a/ja/codes/javascript/chapter_searching/binary_search_insertion.js b/ja/codes/javascript/chapter_searching/binary_search_insertion.js index e0b0a89f3..05041c0d5 100644 --- a/ja/codes/javascript/chapter_searching/binary_search_insertion.js +++ b/ja/codes/javascript/chapter_searching/binary_search_insertion.js @@ -33,7 +33,7 @@ function binarySearchInsertion(nums, target) { } else if (nums[m] > target) { j = m - 1; // target は区間 [i, m-1] にある } else { - j = m - 1; // target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1; // target より小さい最も右の要素は区間 [i, m-1] にある } } // 挿入位置 i を返す diff --git a/ja/codes/kotlin/chapter_searching/binary_search_insertion.kt b/ja/codes/kotlin/chapter_searching/binary_search_insertion.kt index e9a04a2ea..16c0b0df6 100644 --- a/ja/codes/kotlin/chapter_searching/binary_search_insertion.kt +++ b/ja/codes/kotlin/chapter_searching/binary_search_insertion.kt @@ -35,7 +35,7 @@ fun binarySearchInsertion(nums: IntArray, target: Int): Int { } else if (nums[m] > target) { j = m - 1 // target は区間 [i, m-1] にある } else { - j = m - 1 // target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1 // target より小さい最も右の要素は区間 [i, m-1] にある } } // 挿入位置 i を返す @@ -62,4 +62,4 @@ fun main() { val index = binarySearchInsertion(nums, target) println("要素 $target の挿入位置のインデックスは $index") } -} \ No newline at end of file +} diff --git a/ja/codes/python/chapter_searching/binary_search_insertion.py b/ja/codes/python/chapter_searching/binary_search_insertion.py index 3934a742c..48d767262 100644 --- a/ja/codes/python/chapter_searching/binary_search_insertion.py +++ b/ja/codes/python/chapter_searching/binary_search_insertion.py @@ -30,7 +30,7 @@ def binary_search_insertion(nums: list[int], target: int) -> int: elif nums[m] > target: j = m - 1 # target は区間 [i, m-1] にある else: - j = m - 1 # target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1 # target より小さい最も右の要素は区間 [i, m-1] にある # 挿入位置 i を返す return i diff --git a/ja/codes/ruby/chapter_backtracking/permutations_ii.rb b/ja/codes/ruby/chapter_backtracking/permutations_ii.rb index 7c75c02b5..d9a7b6345 100644 --- a/ja/codes/ruby/chapter_backtracking/permutations_ii.rb +++ b/ja/codes/ruby/chapter_backtracking/permutations_ii.rb @@ -4,6 +4,8 @@ Created Time: 2024-05-22 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end +require 'set' + ### バックトラッキング法:全順列 II ### def backtrack(state, choices, selected, res) # 状態の長さが要素数に等しければ、解を記録 diff --git a/ja/codes/ruby/chapter_searching/binary_search_insertion.rb b/ja/codes/ruby/chapter_searching/binary_search_insertion.rb index b68b39752..e4cb43a5f 100644 --- a/ja/codes/ruby/chapter_searching/binary_search_insertion.rb +++ b/ja/codes/ruby/chapter_searching/binary_search_insertion.rb @@ -39,7 +39,7 @@ def binary_search_insertion(nums, target) elsif nums[m] > target j = m - 1 # target は区間 [i, m-1] にある else - j = m - 1 # target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1 # target より小さい最も右の要素は区間 [i, m-1] にある end end diff --git a/ja/codes/rust/chapter_searching/binary_search_insertion.rs b/ja/codes/rust/chapter_searching/binary_search_insertion.rs index 6839f1c55..410b023d6 100644 --- a/ja/codes/rust/chapter_searching/binary_search_insertion.rs +++ b/ja/codes/rust/chapter_searching/binary_search_insertion.rs @@ -32,7 +32,7 @@ pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 { } else if nums[m as usize] > target { j = m - 1; // target は区間 [i, m-1] にある } else { - j = m - 1; // target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1; // target より小さい最も右の要素は区間 [i, m-1] にある } } // 挿入位置 i を返す diff --git a/ja/codes/swift/chapter_searching/binary_search_insertion.swift b/ja/codes/swift/chapter_searching/binary_search_insertion.swift index a0c7e2574..9f9d760e2 100644 --- a/ja/codes/swift/chapter_searching/binary_search_insertion.swift +++ b/ja/codes/swift/chapter_searching/binary_search_insertion.swift @@ -35,7 +35,7 @@ public func binarySearchInsertion(nums: [Int], target: Int) -> Int { } else if nums[m] > target { j = m - 1 // target は区間 [i, m-1] にある } else { - j = m - 1 // target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1 // target より小さい最も右の要素は区間 [i, m-1] にある } } // 挿入位置 i を返す diff --git a/ja/codes/swift/chapter_searching/binary_search_insertion_target.swift b/ja/codes/swift/chapter_searching/binary_search_insertion_target.swift index a0c7e2574..9f9d760e2 100644 --- a/ja/codes/swift/chapter_searching/binary_search_insertion_target.swift +++ b/ja/codes/swift/chapter_searching/binary_search_insertion_target.swift @@ -35,7 +35,7 @@ public func binarySearchInsertion(nums: [Int], target: Int) -> Int { } else if nums[m] > target { j = m - 1 // target は区間 [i, m-1] にある } else { - j = m - 1 // target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1 // target より小さい最も右の要素は区間 [i, m-1] にある } } // 挿入位置 i を返す diff --git a/ja/codes/swift/chapter_stack_and_queue/queue.swift b/ja/codes/swift/chapter_stack_and_queue/queue.swift index 0dfd94cd9..9f341a159 100644 --- a/ja/codes/swift/chapter_stack_and_queue/queue.swift +++ b/ja/codes/swift/chapter_stack_and_queue/queue.swift @@ -26,8 +26,8 @@ enum Queue { /* 要素をデキュー */ // Array を用いる場合、pop の計算量は O(n) - let pool = queue.removeFirst() - print("デキューした要素 pop = \(pool)、デキュー後の queue = \(queue)") + let pop = queue.removeFirst() + print("デキューした要素 pop = \(pop)、デキュー後の queue = \(queue)") /* キューの長さを取得 */ let size = queue.count diff --git a/ja/codes/typescript/chapter_searching/binary_search_insertion.ts b/ja/codes/typescript/chapter_searching/binary_search_insertion.ts index 7d99d5110..bbb30b43f 100644 --- a/ja/codes/typescript/chapter_searching/binary_search_insertion.ts +++ b/ja/codes/typescript/chapter_searching/binary_search_insertion.ts @@ -36,7 +36,7 @@ function binarySearchInsertion(nums: Array, target: number): number { } else if (nums[m] > target) { j = m - 1; // target は区間 [i, m-1] にある } else { - j = m - 1; // target より小さい最初の要素は区間 [i, m-1] にある + j = m - 1; // target より小さい最も右の要素は区間 [i, m-1] にある } } // 挿入位置 i を返す diff --git a/ja/docs/chapter_appendix/installation.md b/ja/docs/chapter_appendix/installation.md index d16901102..b520c9b69 100644 --- a/ja/docs/chapter_appendix/installation.md +++ b/ja/docs/chapter_appendix/installation.md @@ -20,7 +20,7 @@ VS Code には強力な拡張機能のエコシステムがあり、ほとんど ### C/C++ 環境 -1. Windows システムでは [MinGW](https://sourceforge.net/projects/mingw-w64/files/) をインストールする必要があります([設定チュートリアル](https://blog.csdn.net/qq_33698226/article/details/129031241))。MacOS には Clang が標準搭載されているため、追加インストールは不要です。 +1. Windows システムでは [MinGW](https://sourceforge.net/projects/mingw-w64/files/) をインストールする必要があります([設定チュートリアル](https://blog.csdn.net/qq_33698226/article/details/129031241))。macOS には Clang が標準搭載されているため、追加インストールは不要です。 2. VS Code の拡張機能マーケットプレイスで `c++` を検索し、C/C++ Extension Pack をインストールします。 3. (任意)Settings ページを開き、コード整形オプション `Clang_format_fallback Style` を検索して、`{ BasedOnStyle: Microsoft, BreakBeforeBraces: Attach }` に設定します。 @@ -31,7 +31,7 @@ VS Code には強力な拡張機能のエコシステムがあり、ほとんど ### C# 環境 -1. [.Net 8.0](https://dotnet.microsoft.com/en-us/download) をダウンロードしてインストールします。 +1. [.NET 8.0](https://dotnet.microsoft.com/en-us/download) をダウンロードしてインストールします。 2. VS Code の拡張機能マーケットプレイスで `C# Dev Kit` を検索し、C# Dev Kit をインストールします([設定チュートリアル](https://code.visualstudio.com/docs/csharp/get-started))。 3. Visual Studio を使用することもできます([インストール手順](https://learn.microsoft.com/zh-cn/visualstudio/install/install-visual-studio?view=vs-2022))。 diff --git a/ja/docs/chapter_appendix/terminology.md b/ja/docs/chapter_appendix/terminology.md index c456875ba..18bf5c6f2 100644 --- a/ja/docs/chapter_appendix/terminology.md +++ b/ja/docs/chapter_appendix/terminology.md @@ -80,7 +80,9 @@ | level-order traversal | レベル順走査 | | breadth-first traversal | 幅優先走査 | | depth-first traversal | 深さ優先走査 | -| binary search tree | 二分探索木 | +| pre-order traversal | 前順走査 | +| in-order traversal | 中順走査 | +| post-order traversal | 後順走査 | | balanced binary search tree | 平衡二分探索木 | | balance factor | 平衡係数 | | heap | ヒープ | diff --git a/ja/docs/chapter_array_and_linkedlist/linked_list.md b/ja/docs/chapter_array_and_linkedlist/linked_list.md index f3e0af743..2afcb5382 100644 --- a/ja/docs/chapter_array_and_linkedlist/linked_list.md +++ b/ja/docs/chapter_array_and_linkedlist/linked_list.md @@ -166,7 +166,7 @@ // コンストラクタ class ListNode(x: Int) { val _val: Int = x // ノードの値 - val next: ListNode? = null // 次のノードへの参照 + var next: ListNode? = null // 次のノードへの参照 } ``` @@ -658,8 +658,8 @@ // コンストラクタ class ListNode(x: Int) { val _val: Int = x // ノードの値 - val next: ListNode? = null // 後続ノードへの参照 - val prev: ListNode? = null // 前駆ノードへの参照 + var next: ListNode? = null // 後続ノードへの参照 + var prev: ListNode? = null // 前駆ノードへの参照 } ``` diff --git a/ja/docs/chapter_backtracking/permutations_problem.md b/ja/docs/chapter_backtracking/permutations_problem.md index e0b96f228..ae1e32d59 100644 --- a/ja/docs/chapter_backtracking/permutations_problem.md +++ b/ja/docs/chapter_backtracking/permutations_problem.md @@ -30,7 +30,7 @@ 各要素が 1 回しか選ばれないようにするため、ブール配列 `selected` の導入を考えます。ここで `selected[i]` は `choices[i]` がすでに選ばれているかどうかを表し、これに基づいて次の枝刈りを行います。 -- 選択 `choice[i]` を行った後、`selected[i]` を $\text{True}$ に設定し、その要素が選択済みであることを表します。 +- 選択 `choices[i]` を行った後、`selected[i]` を $\text{True}$ に設定し、その要素が選択済みであることを表します。 - 選択肢リスト `choices` を走査するとき、すでに選ばれたノードはすべてスキップします。これが枝刈りです。 下図のように、1 回目に 1、2 回目に 3、3 回目に 2 を選ぶとします。このとき 2 回目では要素 1 の分岐を、3 回目では要素 1 と要素 3 の分岐を刈り取る必要があります。 diff --git a/ja/docs/chapter_data_structure/summary.md b/ja/docs/chapter_data_structure/summary.md index b42ce6cfe..27f942c1c 100644 --- a/ja/docs/chapter_data_structure/summary.md +++ b/ja/docs/chapter_data_structure/summary.md @@ -10,7 +10,7 @@ - 符号付き絶対値表現、1 の補数、2 の補数は、コンピュータで数値を符号化する 3 つの方法であり、相互に変換できます。整数の符号付き絶対値表現では最上位ビットが符号ビットで、残りのビットが数値の値です。 - 整数はコンピュータ内では 2 の補数の形式で格納されます。2 の補数表現では、コンピュータは正数と負数の加算を同じように扱うことができ、減算のために特別なハードウェア回路を別途設計する必要がなく、さらに正負のゼロが重複する問題もありません。 - 浮動小数点数の符号化は、1 ビットの符号部、8 ビットの指数部、23 ビットの仮数部で構成されます。指数部があるため、浮動小数点数の値域は整数よりはるかに広くなりますが、その代償として精度が犠牲になります。 -- ASCII コードは最も早く登場した英字文字集合で、長さは 1 バイト、収録文字数は 127 です。GBK 文字集合はよく使われる中国語文字集合で、2 万字以上の漢字を収録しています。Unicode は完全な文字集合標準を提供することを目指しており、世界中のさまざまな言語の文字を収録することで、文字コード方式の不一致によって生じる文字化けの問題を解決します。 +- ASCII コードは最も早く登場した英字文字集合で、長さは 1 バイト、収録文字数は 128 です。GBK 文字集合はよく使われる中国語文字集合で、2 万字以上の漢字を収録しています。Unicode は完全な文字集合標準を提供することを目指しており、世界中のさまざまな言語の文字を収録することで、文字コード方式の不一致によって生じる文字化けの問題を解決します。 - UTF-8 は最も広く使われている Unicode の符号化方式で、汎用性が非常に高いです。可変長の符号化方式であり、拡張性に優れ、記憶領域の利用効率を効果的に高めます。UTF-16 と UTF-32 は固定長の符号化方式です。中国語を符号化する場合、UTF-16 は UTF-8 よりも使用領域が小さくなります。Java や C# などのプログラミング言語は、デフォルトで UTF-16 を使用します。 ### Q & A diff --git a/ja/docs/chapter_divide_and_conquer/build_binary_tree_problem.md b/ja/docs/chapter_divide_and_conquer/build_binary_tree_problem.md index c61bd21ce..f7a518ad0 100644 --- a/ja/docs/chapter_divide_and_conquer/build_binary_tree_problem.md +++ b/ja/docs/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -21,12 +21,12 @@ 定義に従うと、`preorder` と `inorder` はいずれも 3 つの部分に分けられます。 - 前順走査:`[ 根ノード | 左部分木 | 右部分木 ]` ,例えば上図の木は `[ 3 | 9 | 2 1 7 ]` に対応します。 -- 中順走査:`[ 左部分木 | 根ノード | 右部分木 ]` ,例えば上図の木は `[ 9 | 3 | 1 2 7 ]` に対応します。 +- 中順走査:`[ 左部分木 | 根ノード | 右部分木 ]` ,例えば上図の木は `[ 9 | 3 | 1 2 7 ]` に対応します。 上図のデータを例にすると、下図の手順によって分割結果を得られます。 1. 前順走査の先頭要素 3 が根ノードの値です。 -2. 根ノード 3 の `inorder` におけるインデックスを探すと、そのインデックスを用いて `inorder` を `[ 9 | 3 | 1 2 7 ]` に分割できます。 +2. 根ノード 3 の `inorder` におけるインデックスを探すと、そのインデックスを用いて `inorder` を `[ 9 | 3 | 1 2 7 ]` に分割できます。 3. `inorder` の分割結果から、左部分木と右部分木のノード数はそれぞれ 1 と 3 であることがわかり、したがって `preorder` を `[ 3 | 9 | 2 1 7 ]` に分割できます。 ![前順走査と中順走査で部分木を分割する](build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png) diff --git a/ja/docs/chapter_dynamic_programming/edit_distance_problem.md b/ja/docs/chapter_dynamic_programming/edit_distance_problem.md index 63fff1ed0..2ae7a73c3 100644 --- a/ja/docs/chapter_dynamic_programming/edit_distance_problem.md +++ b/ja/docs/chapter_dynamic_programming/edit_distance_problem.md @@ -35,7 +35,7 @@ 状態 $[i, j]$ に対応する部分問題は、**$s$ の先頭 $i$ 文字を $t$ の先頭 $j$ 文字に変換するのに必要な最小編集回数**です。 -これにより、サイズが $(i+1) \times (j+1)$ の2次元 $dp$ テーブルが得られます。 +これにより、サイズが $(n+1) \times (m+1)$ の2次元 $dp$ テーブルが得られます。 **第2ステップ:最適部分構造を見つけ、状態遷移方程式を導く** diff --git a/ja/docs/chapter_dynamic_programming/knapsack_problem.md b/ja/docs/chapter_dynamic_programming/knapsack_problem.md index 6c41cd752..b498ac7aa 100644 --- a/ja/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/ja/docs/chapter_dynamic_programming/knapsack_problem.md @@ -134,7 +134,7 @@ $$ ### 空間最適化 -各状態は直前の行の状態にしか依存しないため、2つの配列をローテーションして用いることで、空間計算量を $O(n^2)$ から $O(n)$ に削減できます。 +各状態は直前の行の状態にしか依存しないため、2つの配列をローテーションして用いることで、空間計算量を $O(n \times cap)$ から $O(cap)$ に削減できます。 さらに考えると、1つの配列だけで空間最適化を実現できるでしょうか。観察すると、各状態は真上または左上のマスから遷移してきます。配列が1つしかないと仮定すると、$i$ 行目の走査を開始した時点では、その配列にはまだ $i-1$ 行目の状態が格納されています。 diff --git a/ja/docs/chapter_dynamic_programming/summary.md b/ja/docs/chapter_dynamic_programming/summary.md index 9f6ccb68b..815be8906 100644 --- a/ja/docs/chapter_dynamic_programming/summary.md +++ b/ja/docs/chapter_dynamic_programming/summary.md @@ -21,5 +21,5 @@ **編集距離問題** - 編集距離(Levenshtein 距離)は 2 つの文字列間の類似度を測るために用いられ、ある文字列を別の文字列へ変換するための最小編集回数として定義されます。編集操作には追加、削除、置換が含まれます。 -- 編集距離問題の状態は、$s$ の前 $i$ 文字を $t$ の前 $j$ 文字へ変更するのに必要な最小編集回数として定義されます。$s[i] \ne t[j]$ のときは、追加、削除、置換の 3 つの判断があり、それぞれに対応する残りの部分問題があります。これにより最適部分構造を見いだし、状態遷移方程式を構築できます。一方、$s[i] = t[j]$ のときは現在の文字を編集する必要はありません。 +- 編集距離問題の状態は、$s$ の前 $i$ 文字を $t$ の前 $j$ 文字へ変更するのに必要な最小編集回数として定義されます。$s[i-1] \ne t[j-1]$ のときは、追加、削除、置換の 3 つの判断があり、それぞれに対応する残りの部分問題があります。これにより最適部分構造を見いだし、状態遷移方程式を構築できます。一方、$s[i-1] = t[j-1]$ のときは現在の文字を編集する必要はありません。 - 編集距離では、状態は真上、真左、左上の状態に依存します。そのため、空間最適化後は順方向でも逆方向でも正しく状態遷移できません。そこで、変数を 1 つ用いて左上の状態を一時保存し、完全ナップサック問題と等価な形へ変換することで、空間最適化後に順方向走査を行えるようにします。 diff --git a/ja/docs/chapter_greedy/fractional_knapsack_problem.md b/ja/docs/chapter_greedy/fractional_knapsack_problem.md index 0c65ed88c..7c0e993ec 100644 --- a/ja/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/ja/docs/chapter_greedy/fractional_knapsack_problem.md @@ -33,7 +33,7 @@ [file]{fractional_knapsack}-[class]{}-[func]{fractional_knapsack} ``` -組み込みのソートアルゴリズムの時間計算量は通常 $O(\log n)$、空間計算量は通常 $O(\log n)$ または $O(n)$ であり、具体的な値はプログラミング言語の実装に依存する。 +組み込みのソートアルゴリズムの時間計算量は通常 $O(n \log n)$、空間計算量は通常 $O(\log n)$ または $O(n)$ であり、具体的な値はプログラミング言語の実装に依存する。 ソートを除けば、最悪の場合は品物リスト全体を走査する必要があるため、**時間計算量は $O(n)$** であり、ここで $n$ は品物数である。 diff --git a/ja/docs/chapter_greedy/greedy_algorithm.md b/ja/docs/chapter_greedy/greedy_algorithm.md index 126778b9b..ffe8a3c4d 100644 --- a/ja/docs/chapter_greedy/greedy_algorithm.md +++ b/ja/docs/chapter_greedy/greedy_algorithm.md @@ -91,4 +91,4 @@ - **分数ナップサック問題**:一群の品物と積載容量が与えられたとき、総重量が容量を超えず、かつ総価値が最大になるように品物を選ぶ問題です。毎回、価値対重量比(価値 / 重量)が最も高い品物を選ぶなら、ある条件下で貪欲法は最適解を得られます。 - **株式売買問題**:株価の履歴が与えられ、複数回の売買が可能ですが、すでに株を保有している場合は売却前に再度購入することはできません。目標は最大利益を得ることです。 - **ハフマン符号化**:ハフマン符号化は、可逆データ圧縮に用いられる貪欲法です。ハフマン木を構築する際、毎回出現頻度が最も低い 2 つのノードを選んで併合すると、最終的に得られるハフマン木の重み付きパス長(符号長)は最小になります。 -- **Dijkstra アルゴリズム**:与えられた始点から他の各頂点への最短経路問題を解く貪欲法です。 +- **Dijkstra アルゴリズム**:すべての辺の重みが非負であるグラフにおいて、与えられた始点から他の各頂点への最短経路問題を解く貪欲法です。 diff --git a/ja/docs/chapter_greedy/max_product_cutting_problem.md b/ja/docs/chapter_greedy/max_product_cutting_problem.md index 6c88c3b69..a47965242 100644 --- a/ja/docs/chapter_greedy/max_product_cutting_problem.md +++ b/ja/docs/chapter_greedy/max_product_cutting_problem.md @@ -51,7 +51,7 @@ $$ 1. 整数 $n$ を入力し、余りが $0$、$1$、$2$ になるまで、そこから因子 $3$ を繰り返し切り出す。 2. 余りが $0$ のとき、$n$ は $3$ の倍数であることを表すため、何も処理しない。 3. 余りが $2$ のときは、それ以上分割せず、そのまま残す。 -4. 余りが $1$ のとき、$2 \times 2 > 1 \times 3$ であるため、最後の $3$ を $2$ に置き換えるべきである。 +4. 余りが $1$ のとき、$2 \times 2 > 1 \times 3$ であるため、最後の $3$ と余りの $1$ を 2 つの $2$ に置き換えるべきである。 ### コード実装 diff --git a/ja/docs/chapter_hashing/hash_algorithm.md b/ja/docs/chapter_hashing/hash_algorithm.md index 90ef06095..d396871a2 100644 --- a/ja/docs/chapter_hashing/hash_algorithm.md +++ b/ja/docs/chapter_hashing/hash_algorithm.md @@ -160,7 +160,7 @@ $$ size_t hashStr = hash()(str); // 文字列「Hello アルゴリズム」のハッシュ値は 15466937326284535026 - // C++ では、組み込みの std:hash() は基本データ型のハッシュ値計算のみを提供する + // C++ では、組み込みの std::hash() は基本データ型のハッシュ値計算のみを提供する // 配列やオブジェクトのハッシュ値計算は自分で実装する必要がある ``` diff --git a/ja/docs/chapter_hashing/summary.md b/ja/docs/chapter_hashing/summary.md index b3f94df3b..bc2188b95 100644 --- a/ja/docs/chapter_hashing/summary.md +++ b/ja/docs/chapter_hashing/summary.md @@ -10,7 +10,7 @@ - 負荷率は、ハッシュテーブル内の要素数をバケット数で割ったものと定義され、ハッシュ衝突の深刻さを反映する。ハッシュテーブル拡張を発動する条件としてよく用いられる。 - 連鎖方式では、単一要素を連結リストに変換し、衝突したすべての要素を同じ連結リストに格納する。しかし、連結リストが長すぎると検索効率が低下するため、さらに連結リストを赤黒木に変換して効率を高めることができる。 - オープンアドレス法は複数回の探索によってハッシュ衝突を処理する。線形探索は固定のステップ幅を用いるが、要素を削除できず、クラスタリングが発生しやすいという欠点がある。二重ハッシュは複数のハッシュ関数を用いて探索するため、線形探索に比べてクラスタリングが起きにくいが、複数のハッシュ関数によって計算量が増える。 -- プログラミング言語ごとに、異なるハッシュテーブル実装が採用されている。たとえば、Java の `HashMap` は連鎖方式を使用し、Python の `Dict` はオープンアドレス法を採用している。 +- プログラミング言語ごとに、異なるハッシュテーブル実装が採用されている。たとえば、Java の `HashMap` は連鎖方式を使用し、Python の `dict` はオープンアドレス法を採用している。 - ハッシュテーブルでは、ハッシュアルゴリズムに決定性、高効率、均一分布という特徴が求められる。暗号学では、ハッシュアルゴリズムはさらに耐衝突性とアバランシェ効果も備えるべきである。 - ハッシュアルゴリズムは通常、大きな素数を法として用い、ハッシュ値の均一分布を最大限に保証してハッシュ衝突を減らす。 - 一般的なハッシュアルゴリズムには MD5、SHA-1、SHA-2、SHA-3 などがある。MD5 はファイル完全性の検証によく用いられ、SHA-2 はセキュリティ用途やプロトコルでよく用いられる。 diff --git a/ja/docs/chapter_heap/build_heap.md b/ja/docs/chapter_heap/build_heap.md index 404a313c0..362347b09 100644 --- a/ja/docs/chapter_heap/build_heap.md +++ b/ja/docs/chapter_heap/build_heap.md @@ -31,7 +31,7 @@ 以下では、2つ目のヒープ構築法の時間計算量を求めてみましょう。 -- 完全二分木のノード数を $n$ とすると、葉ノード数は $(n + 1) / 2$ です。ここで $/$ は切り捨て除算を表します。したがって、ヒープ化が必要なノード数は $(n - 1) / 2$ です。 +- 完全二分木のノード数を $n$ とすると、葉ノード数は $(n + 1) / 2$ です。ここで $/$ は切り捨て除算を表します。したがって、ヒープ化が必要なノード数は $n / 2$ です。 - 上から下へのヒープ化の過程では、各ノードは最大で葉ノードまでヒープ化されるため、最大反復回数は二分木の高さ $\log n$ です。 上の2つを掛け合わせると、ヒープ構築過程の時間計算量は $O(n \log n)$ となります。**しかし、この見積もりは正確ではありません。二分木では下層のノード数が上層よりはるかに多いという性質を考慮していないためです**。 diff --git a/ja/docs/chapter_searching/binary_search_edge.md b/ja/docs/chapter_searching/binary_search_edge.md index 355191b6d..dd21c5797 100644 --- a/ja/docs/chapter_searching/binary_search_edge.md +++ b/ja/docs/chapter_searching/binary_search_edge.md @@ -41,7 +41,7 @@ ### 要素探索に変換する -配列に `target` が含まれない場合、最終的に $i$ と $j$ はそれぞれ `target` より大きい最初の要素と、`target` より小さい最初の要素を指すことになります。 +配列に `target` が含まれない場合、最終的に $i$ と $j$ はそれぞれ `target` より大きい最初の要素と、`target` より小さい最も右の要素を指すことになります。 したがって、下図のように、配列中に存在しない要素を構成して、それを使って左右の境界を探せます。 diff --git a/ja/docs/chapter_searching/binary_search_insertion.md b/ja/docs/chapter_searching/binary_search_insertion.md index d61940ea3..9ae657f96 100644 --- a/ja/docs/chapter_searching/binary_search_insertion.md +++ b/ja/docs/chapter_searching/binary_search_insertion.md @@ -20,7 +20,7 @@ 二分探索の過程をさらに考えると、`nums[m] < target` のときは $i$ が移動します。これは、ポインタ $i$ が `target` 以上の要素へ近づいていることを意味します。同様に、ポインタ $j$ は常に `target` 以下の要素へ近づいています。 -したがって二分探索の終了時には、$i$ は最初の `target` より大きい要素を指し、$j$ は最初の `target` より小さい要素を指します。**よって、配列に `target` が含まれない場合、挿入インデックスは $i$ です**。コードは次のとおりです: +したがって二分探索の終了時には、$i$ は `target` より大きい最初の要素を指し、$j$ は `target` より小さい最も右の要素を指します。**よって、配列に `target` が含まれない場合、挿入インデックスは $i$ です**。コードは次のとおりです: ```src [file]{binary_search_insertion}-[class]{}-[func]{binary_search_insertion_simple} @@ -48,7 +48,7 @@ - `nums[m] < target` または `nums[m] > target` のときは、まだ `target` を見つけていないことを意味するため、通常の二分探索と同じ区間縮小を行い、**ポインタ $i$ と $j$ を `target` に近づけます**。 - `nums[m] == target` のときは、`target` より小さい要素が区間 $[i, m - 1]$ にあることを意味するため、$j = m - 1$ として区間を縮小し、**ポインタ $j$ を `target` より小さい要素に近づけます**。 -ループ終了後、$i$ は最も左の `target` を指し、$j$ は最初の `target` より小さい要素を指すため、**インデックス $i$ が挿入位置です**。 +ループ終了後、$i$ は最も左の `target` を指し、$j$ は `target` より小さい最も右の要素を指すため、**インデックス $i$ が挿入位置です**。 === "<1>" ![重複要素に対する二分探索の挿入位置の手順](binary_search_insertion.assets/binary_search_insertion_step1.png) diff --git a/ja/docs/chapter_sorting/bucket_sort.md b/ja/docs/chapter_sorting/bucket_sort.md index f2be5438f..30db39d17 100644 --- a/ja/docs/chapter_sorting/bucket_sort.md +++ b/ja/docs/chapter_sorting/bucket_sort.md @@ -1,6 +1,6 @@ # バケットソート -前述のいくつかのソートアルゴリズムは、いずれも「比較ベースのソートアルゴリズム」に属し、要素間の大小を比較することで整列を実現します。この種のソートアルゴリズムの時間計算量は $O(n \log n)$ を超えられません。続いて、時間計算量が線形オーダーに達しうる「非比較ソートアルゴリズム」をいくつか見ていきます。 +前述のいくつかのソートアルゴリズムは、いずれも「比較ベースのソートアルゴリズム」に属し、要素間の大小を比較することで整列を実現します。この種のソートアルゴリズムの最悪時間計算量の下界は $\Omega(n \log n)$ です。続いて、時間計算量が線形オーダーに達しうる「非比較ソートアルゴリズム」をいくつか見ていきます。 バケットソート(bucket sort)は分割統治戦略の典型的な応用です。大小関係をもつ複数のバケットを用意し、各バケットがあるデータ範囲に対応するようにして、データを各バケットへ均等に分配します。その後、各バケット内でそれぞれソートを行い、最後にバケットの順序に従ってすべてのデータを結合します。 diff --git a/ja/docs/chapter_sorting/selection_sort.md b/ja/docs/chapter_sorting/selection_sort.md index 7bbb21425..0aab05c83 100644 --- a/ja/docs/chapter_sorting/selection_sort.md +++ b/ja/docs/chapter_sorting/selection_sort.md @@ -51,7 +51,7 @@ ## アルゴリズムの特徴 -- **時間計算量は $O(n^2)$、非適応ソート**:外側のループは合計 $n - 1$ 回です。最初のラウンドの未ソート区間の長さは $n$、最後のラウンドでは $2$ であり、各ラウンドの内側のループ回数はそれぞれ $n$、$n - 1$、$\dots$、$3$、$2$ となります。総和は $\frac{(n - 1)(n + 2)}{2}$ です。 +- **時間計算量は $O(n^2)$、非適応ソート**:外側のループは合計 $n - 1$ 回です。内側のループは最初のラウンドで $n - 1$ 回、最後のラウンドで $1$ 回実行されます。各ラウンドの実行回数はそれぞれ $n - 1$、$n - 2$、$\dots$、$2$、$1$ であり、総和は $\frac{n(n - 1)}{2}$ です。 - **空間計算量は $O(1)$、インプレースソート**:ポインタ $i$ と $j$ は定数サイズの追加領域しか使用しません。 - **不安定ソート**:次の図のように、要素 `nums[i]` がそれと等しい要素の右側へ交換され、両者の相対的な順序が変わる可能性があります。 diff --git a/ja/docs/chapter_sorting/sorting_algorithm.md b/ja/docs/chapter_sorting/sorting_algorithm.md index a299a0c8f..5b5de3043 100644 --- a/ja/docs/chapter_sorting/sorting_algorithm.md +++ b/ja/docs/chapter_sorting/sorting_algorithm.md @@ -37,7 +37,7 @@ **適応性**:適応的ソートは、入力データに既に存在する順序情報を利用して計算量を減らし、より優れた時間効率を実現できます。適応的ソートアルゴリズムの最良時間計算量は、通常、平均時間計算量より優れています。 -**比較ベースかどうか**:比較ベースのソートは、比較演算子($<$、$=$、$>$)に依存して要素の相対順序を判定し、それによって配列全体をソートします。理論上の最良時間計算量は $O(n \log n)$ です。一方、非比較ソートは比較演算子を使用せず、時間計算量は $O(n)$ に達しますが、汎用性は相対的に低くなります。 +**比較ベースかどうか**:比較ベースのソートは、比較演算子($<$、$=$、$>$)に依存して要素の相対順序を判定し、それによって配列全体をソートします。その最悪時間計算量の下界は $\Omega(n \log n)$ です。一方、非比較ソートは比較演算子を使用せず、時間計算量は $O(n)$ に達しますが、汎用性は相対的に低くなります。 ## 理想的なソートアルゴリズム diff --git a/ja/docs/chapter_stack_and_queue/queue.md b/ja/docs/chapter_stack_and_queue/queue.md index 7766c7aa6..084703f0e 100644 --- a/ja/docs/chapter_stack_and_queue/queue.md +++ b/ja/docs/chapter_stack_and_queue/queue.md @@ -175,7 +175,7 @@ /* 要素をデキュー */ // 配列であるため、removeFirst の計算量は O(n) - let pool = queue.removeFirst() + let pop = queue.removeFirst() /* キューの長さを取得 */ let size = queue.count @@ -244,7 +244,7 @@ ```dart title="queue.dart" /* キューを初期化 */ - // Dart では、キュークラス Qeque は双方向キューであり、キューとしても使用できる + // Dart では、キュークラス Queue は双方向キューであり、キューとしても使用できる Queue queue = Queue(); /* 要素をエンキュー */ diff --git a/ja/docs/chapter_tree/avl_tree.md b/ja/docs/chapter_tree/avl_tree.md index f7763693f..fd652e2ff 100644 --- a/ja/docs/chapter_tree/avl_tree.md +++ b/ja/docs/chapter_tree/avl_tree.md @@ -205,9 +205,9 @@ AVL 木の操作ではノードの高さを取得する必要があるため、 ```kotlin title="" /* AVL 木ノードクラス */ class TreeNode(val _val: Int) { // ノード値 - val height: Int = 0 // ノードの高さ - val left: TreeNode? = null // 左の子ノード - val right: TreeNode? = null // 右の子ノード + var height: Int = 0 // ノードの高さ + var left: TreeNode? = null // 左の子ノード + var right: TreeNode? = null // 右の子ノード } ``` diff --git a/ja/docs/chapter_tree/binary_tree.md b/ja/docs/chapter_tree/binary_tree.md index 479f5b1dc..077d3a77b 100644 --- a/ja/docs/chapter_tree/binary_tree.md +++ b/ja/docs/chapter_tree/binary_tree.md @@ -181,8 +181,8 @@ ```kotlin title="" /* 二分木ノードクラス */ class TreeNode(val _val: Int) { // ノード値 - val left: TreeNode? = null // 左子ノード参照 - val right: TreeNode? = null // 右子ノード参照 + var left: TreeNode? = null // 左子ノード参照 + var right: TreeNode? = null // 右子ノード参照 } ``` diff --git a/ru/codes/c/chapter_searching/binary_search_edge.c b/ru/codes/c/chapter_searching/binary_search_edge.c index 71dbef1bb..23c059b39 100644 --- a/ru/codes/c/chapter_searching/binary_search_edge.c +++ b/ru/codes/c/chapter_searching/binary_search_edge.c @@ -14,7 +14,7 @@ int binarySearchInsertion(int *nums, int numSize, int target) { if (nums[m] < target) { i = m + 1; // target находится в интервале [m+1, j] } else { - j = m - 1; // Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1; // Самый правый элемент, меньший target, находится в интервале [i, m-1] } } // Вернуть точку вставки i @@ -64,4 +64,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/ru/codes/c/chapter_searching/binary_search_insertion.c b/ru/codes/c/chapter_searching/binary_search_insertion.c index 3d99ba0f7..f33ba957d 100644 --- a/ru/codes/c/chapter_searching/binary_search_insertion.c +++ b/ru/codes/c/chapter_searching/binary_search_insertion.c @@ -33,7 +33,7 @@ int binarySearchInsertion(int *nums, int numSize, int target) { } else if (nums[m] > target) { j = m - 1; // target находится в интервале [i, m-1] } else { - j = m - 1; // Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1; // Самый правый элемент, меньший target, находится в интервале [i, m-1] } } // Вернуть точку вставки i diff --git a/ru/codes/c/chapter_stack_and_queue/array_queue.c b/ru/codes/c/chapter_stack_and_queue/array_queue.c index 57ca8e2cc..cb7c9332d 100644 --- a/ru/codes/c/chapter_stack_and_queue/array_queue.c +++ b/ru/codes/c/chapter_stack_and_queue/array_queue.c @@ -131,4 +131,4 @@ int main() { delArrayQueue(queue); return 0; -} +} \ No newline at end of file diff --git a/ru/codes/cpp/chapter_searching/binary_search_edge.cpp b/ru/codes/cpp/chapter_searching/binary_search_edge.cpp index 674db9a8b..aa6b6f486 100644 --- a/ru/codes/cpp/chapter_searching/binary_search_edge.cpp +++ b/ru/codes/cpp/chapter_searching/binary_search_edge.cpp @@ -14,7 +14,7 @@ int binarySearchInsertion(const vector &nums, int target) { if (nums[m] < target) { i = m + 1; // target находится в интервале [m+1, j] } else { - j = m - 1; // Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1; // Самый правый элемент, меньший target, находится в интервале [i, m-1] } } // Вернуть точку вставки i diff --git a/ru/codes/cpp/chapter_searching/binary_search_insertion.cpp b/ru/codes/cpp/chapter_searching/binary_search_insertion.cpp index 60d1afc37..822521962 100644 --- a/ru/codes/cpp/chapter_searching/binary_search_insertion.cpp +++ b/ru/codes/cpp/chapter_searching/binary_search_insertion.cpp @@ -33,7 +33,7 @@ int binarySearchInsertion(vector &nums, int target) { } else if (nums[m] > target) { j = m - 1; // target находится в интервале [i, m-1] } else { - j = m - 1; // Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1; // Самый правый элемент, меньший target, находится в интервале [i, m-1] } } // Вернуть точку вставки i diff --git a/ru/codes/csharp/chapter_searching/binary_search_insertion.cs b/ru/codes/csharp/chapter_searching/binary_search_insertion.cs index d0300c5d7..4f6f0bd3b 100644 --- a/ru/codes/csharp/chapter_searching/binary_search_insertion.cs +++ b/ru/codes/csharp/chapter_searching/binary_search_insertion.cs @@ -34,7 +34,7 @@ public class binary_search_insertion { } else if (nums[m] > target) { j = m - 1; // target находится в интервале [i, m-1] } else { - j = m - 1; // Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1; // Самый правый элемент, меньший target, находится в интервале [i, m-1] } } // Вернуть точку вставки i diff --git a/ru/codes/dart/chapter_searching/binary_search_insertion.dart b/ru/codes/dart/chapter_searching/binary_search_insertion.dart index 31aaf7ea1..936cfd6f9 100644 --- a/ru/codes/dart/chapter_searching/binary_search_insertion.dart +++ b/ru/codes/dart/chapter_searching/binary_search_insertion.dart @@ -31,7 +31,7 @@ int binarySearchInsertion(List nums, int target) { } else if (nums[m] > target) { j = m - 1; // target находится в интервале [i, m-1] } else { - j = m - 1; // Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1; // Самый правый элемент, меньший target, находится в интервале [i, m-1] } } // Вернуть точку вставки i diff --git a/ru/codes/go/chapter_searching/binary_search_insertion.go b/ru/codes/go/chapter_searching/binary_search_insertion.go index e5722dc4b..a3f017130 100644 --- a/ru/codes/go/chapter_searching/binary_search_insertion.go +++ b/ru/codes/go/chapter_searching/binary_search_insertion.go @@ -40,7 +40,7 @@ func binarySearchInsertion(nums []int, target int) int { // target находится в интервале [i, m-1] j = m - 1 } else { - // Первый элемент меньше target находится в интервале [i, m-1] + // Самый правый элемент, меньший target, находится в интервале [i, m-1] j = m - 1 } } diff --git a/ru/codes/java/chapter_searching/binary_search_insertion.java b/ru/codes/java/chapter_searching/binary_search_insertion.java index e358c8c06..b8516ce3d 100644 --- a/ru/codes/java/chapter_searching/binary_search_insertion.java +++ b/ru/codes/java/chapter_searching/binary_search_insertion.java @@ -34,7 +34,7 @@ class binary_search_insertion { } else if (nums[m] > target) { j = m - 1; // target находится в интервале [i, m-1] } else { - j = m - 1; // Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1; // Самый правый элемент, меньший target, находится в интервале [i, m-1] } } // Вернуть точку вставки i diff --git a/ru/codes/javascript/chapter_searching/binary_search_insertion.js b/ru/codes/javascript/chapter_searching/binary_search_insertion.js index d877ec081..98346f2d5 100644 --- a/ru/codes/javascript/chapter_searching/binary_search_insertion.js +++ b/ru/codes/javascript/chapter_searching/binary_search_insertion.js @@ -33,7 +33,7 @@ function binarySearchInsertion(nums, target) { } else if (nums[m] > target) { j = m - 1; // target находится в интервале [i, m-1] } else { - j = m - 1; // Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1; // Самый правый элемент, меньший target, находится в интервале [i, m-1] } } // Вернуть точку вставки i diff --git a/ru/codes/kotlin/chapter_searching/binary_search_insertion.kt b/ru/codes/kotlin/chapter_searching/binary_search_insertion.kt index 6e5710701..82fa6397d 100644 --- a/ru/codes/kotlin/chapter_searching/binary_search_insertion.kt +++ b/ru/codes/kotlin/chapter_searching/binary_search_insertion.kt @@ -35,7 +35,7 @@ fun binarySearchInsertion(nums: IntArray, target: Int): Int { } else if (nums[m] > target) { j = m - 1 // target находится в интервале [i, m-1] } else { - j = m - 1 // Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1 // Самый правый элемент, меньший target, находится в интервале [i, m-1] } } // Вернуть точку вставки i @@ -62,4 +62,4 @@ fun main() { val index = binarySearchInsertion(nums, target) println("Индекс позиции вставки элемента $target равен $index") } -} \ No newline at end of file +} diff --git a/ru/codes/python/chapter_searching/binary_search_insertion.py b/ru/codes/python/chapter_searching/binary_search_insertion.py index 971a4c337..86dfcaa64 100644 --- a/ru/codes/python/chapter_searching/binary_search_insertion.py +++ b/ru/codes/python/chapter_searching/binary_search_insertion.py @@ -30,7 +30,7 @@ def binary_search_insertion(nums: list[int], target: int) -> int: elif nums[m] > target: j = m - 1 # target находится в интервале [i, m-1] else: - j = m - 1 # Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1 # Самый правый элемент, меньший target, находится в интервале [i, m-1] # Вернуть точку вставки i return i diff --git a/ru/codes/ruby/chapter_backtracking/permutations_ii.rb b/ru/codes/ruby/chapter_backtracking/permutations_ii.rb index 3805d66b8..5c9920aee 100644 --- a/ru/codes/ruby/chapter_backtracking/permutations_ii.rb +++ b/ru/codes/ruby/chapter_backtracking/permutations_ii.rb @@ -4,6 +4,8 @@ Created Time: 2024-05-22 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end +require 'set' + ### Алгоритм бэктрекинга: все перестановки II ### def backtrack(state, choices, selected, res) # Когда длина состояния равна числу элементов, записать решение diff --git a/ru/codes/ruby/chapter_searching/binary_search_insertion.rb b/ru/codes/ruby/chapter_searching/binary_search_insertion.rb index 5a9091bbe..11a8e6d21 100644 --- a/ru/codes/ruby/chapter_searching/binary_search_insertion.rb +++ b/ru/codes/ruby/chapter_searching/binary_search_insertion.rb @@ -39,7 +39,7 @@ def binary_search_insertion(nums, target) elsif nums[m] > target j = m - 1 # target находится в интервале [i, m-1] else - j = m - 1 # Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1 # Самый правый элемент, меньший target, находится в интервале [i, m-1] end end diff --git a/ru/codes/rust/chapter_searching/binary_search_insertion.rs b/ru/codes/rust/chapter_searching/binary_search_insertion.rs index 13a9f5756..0d9cdef1e 100644 --- a/ru/codes/rust/chapter_searching/binary_search_insertion.rs +++ b/ru/codes/rust/chapter_searching/binary_search_insertion.rs @@ -32,7 +32,7 @@ pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 { } else if nums[m as usize] > target { j = m - 1; // target находится в интервале [i, m-1] } else { - j = m - 1; // Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1; // Самый правый элемент, меньший target, находится в интервале [i, m-1] } } // Вернуть точку вставки i diff --git a/ru/codes/swift/chapter_searching/binary_search_insertion.swift b/ru/codes/swift/chapter_searching/binary_search_insertion.swift index 57cd71c03..1edeb6a36 100644 --- a/ru/codes/swift/chapter_searching/binary_search_insertion.swift +++ b/ru/codes/swift/chapter_searching/binary_search_insertion.swift @@ -35,7 +35,7 @@ public func binarySearchInsertion(nums: [Int], target: Int) -> Int { } else if nums[m] > target { j = m - 1 // target находится в интервале [i, m-1] } else { - j = m - 1 // Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1 // Самый правый элемент, меньший target, находится в интервале [i, m-1] } } // Вернуть точку вставки i diff --git a/ru/codes/swift/chapter_searching/binary_search_insertion_target.swift b/ru/codes/swift/chapter_searching/binary_search_insertion_target.swift index 57cd71c03..1edeb6a36 100644 --- a/ru/codes/swift/chapter_searching/binary_search_insertion_target.swift +++ b/ru/codes/swift/chapter_searching/binary_search_insertion_target.swift @@ -35,7 +35,7 @@ public func binarySearchInsertion(nums: [Int], target: Int) -> Int { } else if nums[m] > target { j = m - 1 // target находится в интервале [i, m-1] } else { - j = m - 1 // Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1 // Самый правый элемент, меньший target, находится в интервале [i, m-1] } } // Вернуть точку вставки i diff --git a/ru/codes/swift/chapter_stack_and_queue/queue.swift b/ru/codes/swift/chapter_stack_and_queue/queue.swift index 48abbad7d..1522bfc41 100644 --- a/ru/codes/swift/chapter_stack_and_queue/queue.swift +++ b/ru/codes/swift/chapter_stack_and_queue/queue.swift @@ -26,8 +26,8 @@ enum Queue { /* Извлечение элемента из очереди */ // При использовании Array для имитации pop имеет сложность O(n) - let pool = queue.removeFirst() - print("Извлеченный элемент pop = \(pool), queue после извлечения = \(queue)") + let pop = queue.removeFirst() + print("Извлеченный элемент pop = \(pop), queue после извлечения = \(queue)") /* Получение длины очереди */ let size = queue.count diff --git a/ru/codes/typescript/chapter_searching/binary_search_insertion.ts b/ru/codes/typescript/chapter_searching/binary_search_insertion.ts index 20b8bf785..8722633e6 100644 --- a/ru/codes/typescript/chapter_searching/binary_search_insertion.ts +++ b/ru/codes/typescript/chapter_searching/binary_search_insertion.ts @@ -36,7 +36,7 @@ function binarySearchInsertion(nums: Array, target: number): number { } else if (nums[m] > target) { j = m - 1; // target находится в интервале [i, m-1] } else { - j = m - 1; // Первый элемент меньше target находится в интервале [i, m-1] + j = m - 1; // Самый правый элемент, меньший target, находится в интервале [i, m-1] } } // Вернуть точку вставки i diff --git a/ru/docs/chapter_appendix/installation.md b/ru/docs/chapter_appendix/installation.md index 8239a9e9b..359f02ba7 100644 --- a/ru/docs/chapter_appendix/installation.md +++ b/ru/docs/chapter_appendix/installation.md @@ -31,7 +31,7 @@ VS Code обладает мощной экосистемой расширени ### Среда C# -1. Загрузите и установите [.Net 8.0](https://dotnet.microsoft.com/en-us/download). +1. Загрузите и установите [.NET 8.0](https://dotnet.microsoft.com/en-us/download). 2. В магазине расширений VS Code найдите `C# Dev Kit` и установите C# Dev Kit ([руководство по настройке](https://code.visualstudio.com/docs/csharp/get-started)). 3. Также можно использовать Visual Studio ([руководство по установке](https://learn.microsoft.com/zh-cn/visualstudio/install/install-visual-studio?view=vs-2022)). diff --git a/ru/docs/chapter_array_and_linkedlist/linked_list.md b/ru/docs/chapter_array_and_linkedlist/linked_list.md index a7b73ee20..161acba56 100644 --- a/ru/docs/chapter_array_and_linkedlist/linked_list.md +++ b/ru/docs/chapter_array_and_linkedlist/linked_list.md @@ -166,7 +166,7 @@ // Конструктор class ListNode(x: Int) { val _val: Int = x // Значение узла - val next: ListNode? = null // Ссылка на следующий узел + var next: ListNode? = null // Ссылка на следующий узел } ``` @@ -658,8 +658,8 @@ // Конструктор class ListNode(x: Int) { val _val: Int = x // Значение узла - val next: ListNode? = null // Ссылка на следующий узел - val prev: ListNode? = null // Ссылка на предыдущий узел + var next: ListNode? = null // Ссылка на следующий узел + var prev: ListNode? = null // Ссылка на предыдущий узел } ``` diff --git a/ru/docs/chapter_backtracking/permutations_problem.md b/ru/docs/chapter_backtracking/permutations_problem.md index e6149b847..e8f7a5e4c 100644 --- a/ru/docs/chapter_backtracking/permutations_problem.md +++ b/ru/docs/chapter_backtracking/permutations_problem.md @@ -30,7 +30,7 @@ Чтобы гарантировать, что каждый элемент выбирается только один раз, введем булев массив `selected` , где `selected[i]` обозначает, был ли уже выбран `choices[i]` , и на его основе выполним следующую обрезку. -- После того как сделан выбор `choice[i]` , мы присваиваем `selected[i]` значение $\text{True}$ , тем самым отмечая, что этот элемент уже выбран. +- После того как сделан выбор `choices[i]` , мы присваиваем `selected[i]` значение $\text{True}$ , тем самым отмечая, что этот элемент уже выбран. - При обходе списка вариантов `choices` пропускаем все уже выбранные элементы, то есть выполняем обрезку. Как показано на рисунке ниже, если в первом раунде мы выберем 1 , во втором - 3 , а в третьем - 2 , то во втором раунде нужно отсечь ветвь элемента 1 , а в третьем - ветви элементов 1 и 3 . diff --git a/ru/docs/chapter_data_structure/number_encoding.md b/ru/docs/chapter_data_structure/number_encoding.md index 3d1d1bc06..a5fb6c77d 100644 --- a/ru/docs/chapter_data_structure/number_encoding.md +++ b/ru/docs/chapter_data_structure/number_encoding.md @@ -23,8 +23,8 @@ $$ \begin{aligned} & 1 + (-2) \newline -& \rightarrow 0000 \. 0001 + 1000 \. 0010 \newline -& = 1000 \. 0011 \newline +& \rightarrow 0000 \; 0001 + 1000 \; 0010 \newline +& = 1000 \; 0011 \newline & \rightarrow -3 \end{aligned} $$ @@ -34,10 +34,10 @@ $$ $$ \begin{aligned} & 1 + (-2) \newline -& \rightarrow 0000 \. 0001 \. \text{(прямой код)} + 1000 \. 0010 \. \text{(прямой код)} \newline -& = 0000 \. 0001 \. \text{(обратный код)} + 1111 \. 1101 \. \text{(обратный код)} \newline -& = 1111 \. 1110 \. \text{(обратный код)} \newline -& = 1000 \. 0001 \. \text{(прямой код)} \newline +& \rightarrow 0000 \; 0001 \; \text{(прямой код)} + 1000 \; 0010 \; \text{(прямой код)} \newline +& = 0000 \; 0001 \; \text{(обратный код)} + 1111 \; 1101 \; \text{(обратный код)} \newline +& = 1111 \; 1110 \; \text{(обратный код)} \newline +& = 1000 \; 0001 \; \text{(прямой код)} \newline & \rightarrow -1 \end{aligned} $$ @@ -46,8 +46,8 @@ $$ $$ \begin{aligned} -+0 & \rightarrow 0000 \. 0000 \newline --0 & \rightarrow 1000 \. 0000 ++0 & \rightarrow 0000 \; 0000 \newline +-0 & \rightarrow 1000 \; 0000 \end{aligned} $$ @@ -55,25 +55,25 @@ $$ $$ \begin{aligned} --0 \rightarrow \. & 1000 \. 0000 \. \text{(прямой код)} \newline -= \. & 1111 \. 1111 \. \text{(обратный код)} \newline -= 1 \. & 0000 \. 0000 \. \text{(дополнительный код)} \newline +-0 \rightarrow \; & 1000 \; 0000 \; \text{(прямой код)} \newline += \; & 1111 \; 1111 \; \text{(обратный код)} \newline += 1 \; & 0000 \; 0000 \; \text{(дополнительный код)} \newline \end{aligned} $$ -При добавлении $1$ к обратному коду отрицательного нуля возникает перенос, но длина типа `byte` составляет всего 8 бит, поэтому переполнившаяся в 9-й бит единица отбрасывается. Иными словами, **дополнительный код отрицательного нуля равен $0000 \. 0000$ и совпадает с дополнительным кодом положительного нуля**. Значит, в представлении дополнительного кода существует только один ноль, и проблема неоднозначности положительного и отрицательного нуля тем самым устраняется. +При добавлении $1$ к обратному коду отрицательного нуля возникает перенос, но длина типа `byte` составляет всего 8 бит, поэтому переполнившаяся в 9-й бит единица отбрасывается. Иными словами, **дополнительный код отрицательного нуля равен $0000 \; 0000$ и совпадает с дополнительным кодом положительного нуля**. Значит, в представлении дополнительного кода существует только один ноль, и проблема неоднозначности положительного и отрицательного нуля тем самым устраняется. Остается последний вопрос: диапазон типа `byte` равен $[-128, 127]$ , откуда берется лишнее отрицательное число $-128$ ? Мы замечаем, что у всех целых чисел из интервала $[-127, +127]$ есть соответствующие прямой, обратный и дополнительный коды, а прямой и дополнительный коды можно преобразовывать друг в друга. -Однако **дополнительный код $1000 \. 0000$ является исключением: у него нет соответствующего прямого кода**. Согласно правилу преобразования, прямой код для этого дополнительного кода должен быть равен $0000 \. 0000$ . Это очевидное противоречие, потому что такой прямой код обозначает число $0$ , а его дополнительный код должен совпадать с ним самим. Компьютер просто определяет, что этот особый дополнительный код $1000 \. 0000$ представляет число $-128$ . На самом деле результат вычисления $(-1) + (-127)$ в дополнительном коде как раз и равен $-128$ . +Однако **дополнительный код $1000 \; 0000$ является исключением: у него нет соответствующего прямого кода**. Согласно правилу преобразования, прямой код для этого дополнительного кода должен быть равен $0000 \; 0000$ . Это очевидное противоречие, потому что такой прямой код обозначает число $0$ , а его дополнительный код должен совпадать с ним самим. Компьютер просто определяет, что этот особый дополнительный код $1000 \; 0000$ представляет число $-128$ . На самом деле результат вычисления $(-1) + (-127)$ в дополнительном коде как раз и равен $-128$ . $$ \begin{aligned} & (-127) + (-1) \newline -& \rightarrow 1111 \. 1111 \. \text{(прямой код)} + 1000 \. 0001 \. \text{(прямой код)} \newline -& = 1000 \. 0000 \. \text{(обратный код)} + 1111 \. 1110 \. \text{(обратный код)} \newline -& = 1000 \. 0001 \. \text{(дополнительный код)} + 1111 \. 1111 \. \text{(дополнительный код)} \newline -& = 1000 \. 0000 \. \text{(дополнительный код)} \newline +& \rightarrow 1111 \; 1111 \; \text{(прямой код)} + 1000 \; 0001 \; \text{(прямой код)} \newline +& = 1000 \; 0000 \; \text{(обратный код)} + 1111 \; 1110 \; \text{(обратный код)} \newline +& = 1000 \; 0001 \; \text{(дополнительный код)} + 1111 \; 1111 \; \text{(дополнительный код)} \newline +& = 1000 \; 0000 \; \text{(дополнительный код)} \newline & \rightarrow -128 \end{aligned} $$ diff --git a/ru/docs/chapter_data_structure/summary.md b/ru/docs/chapter_data_structure/summary.md index da0e678e5..bd5839dcd 100644 --- a/ru/docs/chapter_data_structure/summary.md +++ b/ru/docs/chapter_data_structure/summary.md @@ -10,7 +10,7 @@ - Прямой код, обратный код и дополнительный код - это три способа кодирования чисел в компьютере, между которыми можно выполнять взаимные преобразования. В прямом коде старший бит целого числа является знаковым, а остальные биты представляют значение числа. - Целые числа в компьютере хранятся в виде дополнительного кода. В таком представлении компьютер может одинаково обрабатывать сложение положительных и отрицательных чисел без специальной аппаратной схемы для вычитания, и при этом исчезает неоднозначность положительного и отрицательного нуля. - Кодирование числа с плавающей точкой состоит из 1 бита знака, 8 битов экспоненты и 23 битов мантиссы. Благодаря наличию экспоненты диапазон значений у чисел с плавающей точкой намного больше, чем у целых, но это достигается ценой потери точности. -- ASCII - это самый ранний набор английских символов длиной 1 байт, включающий в общей сложности 127 символов. Набор GBK - распространенный китайский набор символов, включающий более двадцати тысяч иероглифов. Unicode стремится предоставить единый полный стандарт набора символов, включающий символы всех языков мира, чтобы решить проблемы искаженного текста, вызванные несовместимыми способами кодирования. +- ASCII - это самый ранний набор английских символов длиной 1 байт, включающий в общей сложности 128 символов. Набор GBK - распространенный китайский набор символов, включающий более двадцати тысяч иероглифов. Unicode стремится предоставить единый полный стандарт набора символов, включающий символы всех языков мира, чтобы решить проблемы искаженного текста, вызванные несовместимыми способами кодирования. - UTF-8 - самый популярный способ кодирования Unicode, обладающий очень хорошей универсальностью. Это кодировка переменной длины, хорошо расширяемая и эффективно использующая память. UTF-16 и UTF-32 относятся к кодировкам фиксированной длины. При кодировании китайского текста UTF-16 занимает меньше места, чем UTF-8. Такие языки программирования, как Java и C#, по умолчанию используют UTF-16. ### Q & A diff --git a/ru/docs/chapter_dynamic_programming/edit_distance_problem.md b/ru/docs/chapter_dynamic_programming/edit_distance_problem.md index 01506ca96..a96b8c906 100644 --- a/ru/docs/chapter_dynamic_programming/edit_distance_problem.md +++ b/ru/docs/chapter_dynamic_programming/edit_distance_problem.md @@ -35,7 +35,7 @@ Подзадача, соответствующая состоянию $[i, j]$ , такова: **минимальное число операций редактирования, необходимое для преобразования первых $i$ символов строки $s$ в первые $j$ символов строки $t$**. -Отсюда получается двумерная таблица $dp$ размера $(i+1) \times (j+1)$ . +Отсюда получается двумерная таблица $dp$ размера $(n+1) \times (m+1)$ . **Шаг 2: найти оптимальную подструктуру и на ее основе вывести уравнение перехода состояния** diff --git a/ru/docs/chapter_dynamic_programming/knapsack_problem.md b/ru/docs/chapter_dynamic_programming/knapsack_problem.md index e79792943..6c4c668e3 100644 --- a/ru/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/ru/docs/chapter_dynamic_programming/knapsack_problem.md @@ -134,7 +134,7 @@ $$ ### Оптимизация пространства -Поскольку каждое состояние зависит только от состояния в предыдущей строке, можно использовать два массива, которые будут продвигаться вперед по очереди, и тем самым уменьшить пространственную сложность с $O(n^2)$ до $O(n)$ . +Поскольку каждое состояние зависит только от состояния в предыдущей строке, можно использовать два массива, которые будут продвигаться вперед по очереди, и тем самым уменьшить пространственную сложность с $O(n \times cap)$ до $O(cap)$ . Если пойти дальше, можно спросить: можно ли оптимизировать память так, чтобы использовать только один массив? Наблюдение показывает, что каждое состояние зависит от клетки прямо сверху и клетки слева сверху. Предположим, что у нас есть только один массив, и в момент начала обхода строки $i$ он еще хранит состояния строки $i-1$ . diff --git a/ru/docs/chapter_dynamic_programming/summary.md b/ru/docs/chapter_dynamic_programming/summary.md index 8ee7f3f61..4de9ff4d1 100644 --- a/ru/docs/chapter_dynamic_programming/summary.md +++ b/ru/docs/chapter_dynamic_programming/summary.md @@ -21,5 +21,5 @@ **Задача о расстоянии редактирования** - Расстояние редактирования (расстояние Левенштейна) используется для измерения сходства двух строк и определяется как минимальное число операций редактирования, необходимых для преобразования одной строки в другую. Допустимые операции - вставка, удаление и замена. -- В задаче о расстоянии редактирования состояние определяется как минимальное число шагов редактирования, необходимых для преобразования первых $i$ символов строки $s$ в первые $j$ символов строки $t$ . Если $s[i] \ne t[j]$ , то существуют три решения: вставка, удаление и замена, и каждому из них соответствует своя остаточная подзадача. На этой основе выводятся оптимальная подструктура и уравнение перехода состояния. Если же $s[i] = t[j]$ , то редактировать текущий символ не нужно. +- В задаче о расстоянии редактирования состояние определяется как минимальное число шагов редактирования, необходимых для преобразования первых $i$ символов строки $s$ в первые $j$ символов строки $t$ . Если $s[i-1] \ne t[j-1]$ , то существуют три решения: вставка, удаление и замена, и каждому из них соответствует своя остаточная подзадача. На этой основе выводятся оптимальная подструктура и уравнение перехода состояния. Если же $s[i-1] = t[j-1]$ , то редактировать текущий символ не нужно. - В задаче о расстоянии редактирования состояние зависит от значений сверху, слева и слева сверху. Поэтому после оптимизации памяти ни прямой, ни обратный обход сам по себе не дает корректного перехода состояния. Для решения этой проблемы значение слева сверху временно сохраняется в отдельной переменной, что делает ситуацию эквивалентной задаче о полном рюкзаке и позволяет использовать прямой обход. diff --git a/ru/docs/chapter_greedy/greedy_algorithm.md b/ru/docs/chapter_greedy/greedy_algorithm.md index 7e9be26cf..950cb6491 100644 --- a/ru/docs/chapter_greedy/greedy_algorithm.md +++ b/ru/docs/chapter_greedy/greedy_algorithm.md @@ -91,4 +91,4 @@ - **Задача о дробном рюкзаке**: дана группа предметов и грузоподъемность. Требуется выбрать предметы так, чтобы их общий вес не превышал ограничение, а общая ценность была максимальной. Если каждый раз выбирать предмет с наилучшим отношением стоимости к весу, то в некоторых случаях жадный алгоритм дает оптимальный ответ. - **Задача о покупке и продаже акций**: дана история цен акции. Можно совершать несколько сделок, но если акция уже куплена, то до продажи покупать снова нельзя. Цель - получить максимальную прибыль. - **Код Хаффмана**: это жадный алгоритм для сжатия данных без потерь. Построив дерево Хаффмана и каждый раз объединяя два узла с наименьшей частотой, мы получаем дерево с минимальной взвешенной длиной пути, то есть минимальной длиной кодирования. -- **Алгоритм Дейкстры**: это жадный алгоритм решения задачи о кратчайших путях от заданной исходной вершины до всех остальных вершин. +- **Алгоритм Дейкстры**: для графов с неотрицательными весами ребер это жадный алгоритм решения задачи о кратчайших путях от заданной исходной вершины до всех остальных вершин. diff --git a/ru/docs/chapter_greedy/max_product_cutting_problem.md b/ru/docs/chapter_greedy/max_product_cutting_problem.md index c27d1b036..b7610a56f 100644 --- a/ru/docs/chapter_greedy/max_product_cutting_problem.md +++ b/ru/docs/chapter_greedy/max_product_cutting_problem.md @@ -51,7 +51,7 @@ $$ 1. Для заданного целого $n$ непрерывно выделять из него множитель $3$, пока остаток не станет равным $0$, $1$ или $2$. 2. Если остаток равен $0$, это означает, что $n$ кратно $3$, и больше ничего делать не нужно. 3. Если остаток равен $2$, дальнейшее разбиение не требуется, его нужно сохранить. -4. Если остаток равен $1$, то поскольку $2 \times 2 > 1 \times 3$, последний множитель $3$ следует заменить на $2$. +4. Если остаток равен $1$, то поскольку $2 \times 2 > 1 \times 3$, последний множитель $3$ и оставшуюся единицу следует заменить двумя множителями $2$. ### Код реализации diff --git a/ru/docs/chapter_hashing/summary.md b/ru/docs/chapter_hashing/summary.md index efe5ff014..21582b4c1 100644 --- a/ru/docs/chapter_hashing/summary.md +++ b/ru/docs/chapter_hashing/summary.md @@ -10,7 +10,7 @@ - Коэффициент загрузки определяется как отношение числа элементов в хеш-таблице к числу бакетов, отражает степень серьезности хеш-коллизий и часто используется как условие запуска расширения хеш-таблицы. - Метод цепочек превращает одиночный элемент в связный список и хранит все конфликтующие элементы в одном списке. Однако слишком длинный список снижает эффективность поиска, поэтому его можно дополнительно преобразовать в красно-черное дерево. - Открытая адресация обрабатывает хеш-коллизии за счет многократного пробирования. Линейное пробирование использует фиксированный шаг, его недостатки - невозможность прямого удаления элементов и склонность к кластеризации. Повторное хеширование использует несколько хеш-функций и по сравнению с линейным пробированием меньше подвержено кластеризации, но требует больше вычислений. -- Разные языки программирования выбирают разные стратегии реализации хеш-таблиц. Например, `HashMap` в Java использует метод цепочек, а `Dict` в Python - открытую адресацию. +- Разные языки программирования выбирают разные стратегии реализации хеш-таблиц. Например, `HashMap` в Java использует метод цепочек, а `dict` в Python - открытую адресацию. - Для хеш-таблицы желательно, чтобы хеш-алгоритм был детерминированным, быстрым и обеспечивал равномерное распределение. В криптографии от него дополнительно требуют устойчивости к коллизиям и эффекта лавины. - В качестве модуля хеш-алгоритмы обычно используют большое простое число, чтобы максимально обеспечить равномерность распределения хеш-значений и снизить число хеш-коллизий. - К распространенным хеш-алгоритмам относятся MD5, SHA-1, SHA-2 и SHA-3. MD5 часто применяли для проверки целостности файлов, а SHA-2 широко используется в протоколах и приложениях, связанных с безопасностью. diff --git a/ru/docs/chapter_heap/build_heap.md b/ru/docs/chapter_heap/build_heap.md index 8f75ade3b..ff50f6a24 100644 --- a/ru/docs/chapter_heap/build_heap.md +++ b/ru/docs/chapter_heap/build_heap.md @@ -31,7 +31,7 @@ Теперь попробуем оценить временную сложность второго способа построения кучи. -- Пусть число узлов полного двоичного дерева равно $n$ , тогда число листовых узлов равно $(n + 1) / 2$ , где $/$ означает целочисленное деление вниз. Следовательно, число узлов, которые нужно упорядочивать, равно $(n - 1) / 2$ . +- Пусть число узлов полного двоичного дерева равно $n$ , тогда число листовых узлов равно $(n + 1) / 2$ , где $/$ означает целочисленное деление вниз. Следовательно, число узлов, которые нужно упорядочивать, равно $n / 2$ . - В процессе упорядочивания сверху вниз каждый узел в худшем случае может просеяться до листа, поэтому максимальное число итераций равно высоте двоичного дерева $\log n$ . Перемножив эти два значения, можно получить временную сложность построения кучи $O(n \log n)$ . **Но эта оценка неточна, потому что мы не учли свойство двоичного дерева: на нижних уровнях узлов гораздо больше, чем на верхних**. diff --git a/ru/docs/chapter_searching/binary_search_edge.md b/ru/docs/chapter_searching/binary_search_edge.md index 69c44e18d..543c88354 100644 --- a/ru/docs/chapter_searching/binary_search_edge.md +++ b/ru/docs/chapter_searching/binary_search_edge.md @@ -41,7 +41,7 @@ ### Преобразование в поиск элемента -Мы знаем, что если массив не содержит `target` , то в конце поиска указатели $i$ и $j$ будут указывать соответственно на первый элемент, больший `target` , и на первый элемент, меньший `target` . +Мы знаем, что если массив не содержит `target` , то в конце поиска указатели $i$ и $j$ будут указывать соответственно на первый элемент, больший `target` , и на самый правый элемент, меньший `target` . Следовательно, как показано на рисунке ниже, для поиска левой и правой границы можно сконструировать элемент, которого нет в массиве. diff --git a/ru/docs/chapter_searching/binary_search_insertion.md b/ru/docs/chapter_searching/binary_search_insertion.md index 23d4b62e9..a31e7f879 100644 --- a/ru/docs/chapter_searching/binary_search_insertion.md +++ b/ru/docs/chapter_searching/binary_search_insertion.md @@ -20,7 +20,7 @@ Рассмотрим процесс двоичного поиска подробнее: когда `nums[m] < target` , указатель $i$ сдвигается вправо и тем самым приближается к элементу, который больше либо равен `target` . Аналогично указатель $j$ постепенно приближается к элементу, который меньше либо равен `target` . -Следовательно, после завершения двоичного поиска обязательно выполняется следующее: указатель $i$ указывает на первый элемент, больший `target` , а указатель $j$ указывает на первый элемент, меньший `target` . **Нетрудно сделать вывод, что если массив не содержит `target` , то индекс вставки равен $i$** . Код приведен ниже: +Следовательно, после завершения двоичного поиска обязательно выполняется следующее: указатель $i$ указывает на первый элемент, больший `target` , а указатель $j$ указывает на самый правый элемент, меньший `target` . **Нетрудно сделать вывод, что если массив не содержит `target` , то индекс вставки равен $i$** . Код приведен ниже: ```src [file]{binary_search_insertion}-[class]{}-[func]{binary_search_insertion_simple} @@ -48,7 +48,7 @@ - Когда `nums[m] < target` или `nums[m] > target` , это означает, что `target` еще не найден, поэтому используется стандартная операция сужения интервала в двоичном поиске, **благодаря чему указатели $i$ и $j$ приближаются к `target`**. - Когда `nums[m] == target` , это означает, что элементы меньше `target` находятся в интервале $[i, m - 1]$ , поэтому мы используем $j = m - 1$ для сужения интервала, **тем самым приближая указатель $j$ к элементам, меньшим `target`**. -После завершения цикла указатель $i$ будет указывать на самый левый `target` , а указатель $j$ - на первый элемент, меньший `target` , **поэтому индекс $i$ и является точкой вставки**. +После завершения цикла указатель $i$ будет указывать на самый левый `target` , а указатель $j$ - на самый правый элемент, меньший `target` , **поэтому индекс $i$ и является точкой вставки**. === "<1>" ![Шаги поиска точки вставки для повторяющихся элементов](binary_search_insertion.assets/binary_search_insertion_step1.png) diff --git a/ru/docs/chapter_sorting/bucket_sort.md b/ru/docs/chapter_sorting/bucket_sort.md index 163cb6333..2f66acc11 100644 --- a/ru/docs/chapter_sorting/bucket_sort.md +++ b/ru/docs/chapter_sorting/bucket_sort.md @@ -1,6 +1,6 @@ # Блочная сортировка -Рассмотренные выше алгоритмы сортировки относятся к «сортировкам на основе сравнений»: они упорядочивают данные, сравнивая элементы друг с другом. Временная сложность таких алгоритмов не может быть лучше $O(n \log n)$ . Далее мы рассмотрим несколько «сортировок без сравнений», чья временная сложность может достигать линейного порядка. +Рассмотренные выше алгоритмы сортировки относятся к «сортировкам на основе сравнений»: они упорядочивают данные, сравнивая элементы друг с другом. Нижняя граница их временной сложности в худшем случае равна $\Omega(n \log n)$ . Далее мы рассмотрим несколько «сортировок без сравнений», чья временная сложность может достигать линейного порядка. Блочная сортировка (bucket sort) является типичным применением стратегии «разделяй и властвуй». Она создает набор упорядоченных по величине блоков, где каждый блок соответствует определенному диапазону данных. Затем элементы равномерно распределяются по этим блокам, внутри каждого блока отдельно выполняется сортировка, а в конце результаты объединяются в порядке блоков. diff --git a/ru/docs/chapter_sorting/selection_sort.md b/ru/docs/chapter_sorting/selection_sort.md index ce374b804..f15ce5fa1 100644 --- a/ru/docs/chapter_sorting/selection_sort.md +++ b/ru/docs/chapter_sorting/selection_sort.md @@ -51,7 +51,7 @@ ## Характеристики алгоритма -- **Временная сложность равна $O(n^2)$, сортировка не является адаптивной**: внешний цикл выполняется $n - 1$ раз. В первом раунде длина неотсортированного диапазона равна $n$ , а в последнем - $2$ , то есть отдельные раунды содержат $n$, $n - 1$, $\dots$, $3$, $2$ проходов внутреннего цикла, их сумма равна $\frac{(n - 1)(n + 2)}{2}$ . +- **Временная сложность равна $O(n^2)$, сортировка не является адаптивной**: внешний цикл выполняется $n - 1$ раз. Внутренний цикл выполняется $n - 1$ раз в первом раунде и $1$ раз в последнем. Таким образом, число итераций внутреннего цикла по раундам равно $n - 1$, $n - 2$, $\dots$, $2$, $1$, а их сумма равна $\frac{n(n - 1)}{2}$ . - **Пространственная сложность равна $O(1)$, сортировка выполняется на месте**: указатели $i$ и $j$ используют константный объем дополнительной памяти. - **Нестабильная сортировка**: как показано на рисунке ниже, элемент `nums[i]` может быть переставлен вправо от другого равного ему элемента, из-за чего их относительный порядок изменится. diff --git a/ru/docs/chapter_sorting/sorting_algorithm.md b/ru/docs/chapter_sorting/sorting_algorithm.md index 4c0da70f8..47cf0747c 100644 --- a/ru/docs/chapter_sorting/sorting_algorithm.md +++ b/ru/docs/chapter_sorting/sorting_algorithm.md @@ -37,7 +37,7 @@ **Адаптивность**: адаптивная сортировка умеет использовать уже существующий порядок входных данных, чтобы сократить вычисления и добиться лучшей эффективности. Лучшая временная сложность адаптивных алгоритмов обычно лучше их средней временной сложности. -**Основанность на сравнении**: сортировка на основе сравнений использует операторы сравнения ($<$, $=$, $>$), чтобы определить относительный порядок элементов и отсортировать массив. Ее теоретически лучшая временная сложность равна $O(n \log n)$ . А вот сортировка без сравнений не опирается на операторы сравнения, поэтому может достигать $O(n)$ , но универсальность у нее ниже. +**Основанность на сравнении**: сортировка на основе сравнений использует операторы сравнения ($<$, $=$, $>$), чтобы определить относительный порядок элементов и отсортировать массив. Нижняя граница ее временной сложности в худшем случае равна $\Omega(n \log n)$ . А вот сортировка без сравнений не опирается на операторы сравнения, поэтому может достигать $O(n)$ , но универсальность у нее ниже. ## Идеальный алгоритм сортировки diff --git a/ru/docs/chapter_stack_and_queue/queue.md b/ru/docs/chapter_stack_and_queue/queue.md index e5244490c..9d8fd9e57 100644 --- a/ru/docs/chapter_stack_and_queue/queue.md +++ b/ru/docs/chapter_stack_and_queue/queue.md @@ -175,7 +175,7 @@ /* Извлечь элемент из очереди */ // Поскольку в основе лежит массив, removeFirst имеет сложность O(n) - let pool = queue.removeFirst() + let pop = queue.removeFirst() /* Получить длину очереди */ let size = queue.count diff --git a/ru/docs/chapter_tree/avl_tree.md b/ru/docs/chapter_tree/avl_tree.md index 8aafbb532..d617bb4bb 100644 --- a/ru/docs/chapter_tree/avl_tree.md +++ b/ru/docs/chapter_tree/avl_tree.md @@ -205,9 +205,9 @@ AVL-дерево одновременно является и двоичным ```kotlin title="" /* Класс узла AVL-дерева */ class TreeNode(val _val: Int) { // Значение узла - val height: Int = 0 // Высота узла - val left: TreeNode? = null // Левый дочерний узел - val right: TreeNode? = null // Правый дочерний узел + var height: Int = 0 // Высота узла + var left: TreeNode? = null // Левый дочерний узел + var right: TreeNode? = null // Правый дочерний узел } ``` diff --git a/ru/docs/chapter_tree/binary_tree.md b/ru/docs/chapter_tree/binary_tree.md index b47ea13e9..19137ce97 100644 --- a/ru/docs/chapter_tree/binary_tree.md +++ b/ru/docs/chapter_tree/binary_tree.md @@ -181,8 +181,8 @@ ```kotlin title="" /* Класс узла двоичного дерева */ class TreeNode(val _val: Int) { // Значение узла - val left: TreeNode? = null // Ссылка на левого дочернего узла - val right: TreeNode? = null // Ссылка на правого дочернего узла + var left: TreeNode? = null // Ссылка на левого дочернего узла + var right: TreeNode? = null // Ссылка на правого дочернего узла } ``` diff --git a/zh-hant/codes/c/chapter_searching/binary_search_edge.c b/zh-hant/codes/c/chapter_searching/binary_search_edge.c index d1285fe9b..d40f873de 100644 --- a/zh-hant/codes/c/chapter_searching/binary_search_edge.c +++ b/zh-hant/codes/c/chapter_searching/binary_search_edge.c @@ -14,7 +14,7 @@ int binarySearchInsertion(int *nums, int numSize, int target) { if (nums[m] < target) { i = m + 1; // target 在區間 [m+1, j] 中 } else { - j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中 } } // 返回插入點 i @@ -64,4 +64,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/zh-hant/codes/c/chapter_searching/binary_search_insertion.c b/zh-hant/codes/c/chapter_searching/binary_search_insertion.c index 67ea0784e..7c5216437 100644 --- a/zh-hant/codes/c/chapter_searching/binary_search_insertion.c +++ b/zh-hant/codes/c/chapter_searching/binary_search_insertion.c @@ -33,7 +33,7 @@ int binarySearchInsertion(int *nums, int numSize, int target) { } else if (nums[m] > target) { j = m - 1; // target 在區間 [i, m-1] 中 } else { - j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中 } } // 返回插入點 i diff --git a/zh-hant/codes/cpp/chapter_hashing/built_in_hash.cpp b/zh-hant/codes/cpp/chapter_hashing/built_in_hash.cpp index 6616f5c6e..8804a475d 100644 --- a/zh-hant/codes/cpp/chapter_hashing/built_in_hash.cpp +++ b/zh-hant/codes/cpp/chapter_hashing/built_in_hash.cpp @@ -24,6 +24,6 @@ int main() { size_t hashStr = hash()(str); cout << "字串 " << str << " 的雜湊值為 " << hashStr << "\n"; - // 在 C++ 中,內建 std:hash() 僅提供基本資料型別的雜湊值計算 + // 在 C++ 中,內建 std::hash() 僅提供基本資料型別的雜湊值計算 // 陣列、物件的雜湊值計算需要自行實現 } diff --git a/zh-hant/codes/cpp/chapter_searching/binary_search_edge.cpp b/zh-hant/codes/cpp/chapter_searching/binary_search_edge.cpp index e39607abe..acf182337 100644 --- a/zh-hant/codes/cpp/chapter_searching/binary_search_edge.cpp +++ b/zh-hant/codes/cpp/chapter_searching/binary_search_edge.cpp @@ -14,7 +14,7 @@ int binarySearchInsertion(const vector &nums, int target) { if (nums[m] < target) { i = m + 1; // target 在區間 [m+1, j] 中 } else { - j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中 } } // 返回插入點 i diff --git a/zh-hant/codes/cpp/chapter_searching/binary_search_insertion.cpp b/zh-hant/codes/cpp/chapter_searching/binary_search_insertion.cpp index e0526125d..d63eca408 100644 --- a/zh-hant/codes/cpp/chapter_searching/binary_search_insertion.cpp +++ b/zh-hant/codes/cpp/chapter_searching/binary_search_insertion.cpp @@ -33,7 +33,7 @@ int binarySearchInsertion(vector &nums, int target) { } else if (nums[m] > target) { j = m - 1; // target 在區間 [i, m-1] 中 } else { - j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中 } } // 返回插入點 i diff --git a/zh-hant/codes/csharp/chapter_searching/binary_search_insertion.cs b/zh-hant/codes/csharp/chapter_searching/binary_search_insertion.cs index 8b4659ac7..c7bd202de 100644 --- a/zh-hant/codes/csharp/chapter_searching/binary_search_insertion.cs +++ b/zh-hant/codes/csharp/chapter_searching/binary_search_insertion.cs @@ -34,7 +34,7 @@ public class binary_search_insertion { } else if (nums[m] > target) { j = m - 1; // target 在區間 [i, m-1] 中 } else { - j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中 } } // 返回插入點 i diff --git a/zh-hant/codes/dart/chapter_searching/binary_search_insertion.dart b/zh-hant/codes/dart/chapter_searching/binary_search_insertion.dart index 4104243a4..46332276c 100644 --- a/zh-hant/codes/dart/chapter_searching/binary_search_insertion.dart +++ b/zh-hant/codes/dart/chapter_searching/binary_search_insertion.dart @@ -31,7 +31,7 @@ int binarySearchInsertion(List nums, int target) { } else if (nums[m] > target) { j = m - 1; // target 在區間 [i, m-1] 中 } else { - j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中 } } // 返回插入點 i diff --git a/zh-hant/codes/go/chapter_searching/binary_search_insertion.go b/zh-hant/codes/go/chapter_searching/binary_search_insertion.go index 1aef06084..7bf17e0d9 100644 --- a/zh-hant/codes/go/chapter_searching/binary_search_insertion.go +++ b/zh-hant/codes/go/chapter_searching/binary_search_insertion.go @@ -40,7 +40,7 @@ func binarySearchInsertion(nums []int, target int) int { // target 在區間 [i, m-1] 中 j = m - 1 } else { - // 首個小於 target 的元素在區間 [i, m-1] 中 + // 最右一個小於 target 的元素在區間 [i, m-1] 中 j = m - 1 } } diff --git a/zh-hant/codes/java/chapter_searching/binary_search_insertion.java b/zh-hant/codes/java/chapter_searching/binary_search_insertion.java index 445ae13ce..eec280300 100644 --- a/zh-hant/codes/java/chapter_searching/binary_search_insertion.java +++ b/zh-hant/codes/java/chapter_searching/binary_search_insertion.java @@ -34,7 +34,7 @@ class binary_search_insertion { } else if (nums[m] > target) { j = m - 1; // target 在區間 [i, m-1] 中 } else { - j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中 } } // 返回插入點 i diff --git a/zh-hant/codes/javascript/chapter_searching/binary_search_insertion.js b/zh-hant/codes/javascript/chapter_searching/binary_search_insertion.js index 32779c54b..6667f44ca 100644 --- a/zh-hant/codes/javascript/chapter_searching/binary_search_insertion.js +++ b/zh-hant/codes/javascript/chapter_searching/binary_search_insertion.js @@ -33,7 +33,7 @@ function binarySearchInsertion(nums, target) { } else if (nums[m] > target) { j = m - 1; // target 在區間 [i, m-1] 中 } else { - j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中 } } // 返回插入點 i diff --git a/zh-hant/codes/kotlin/chapter_searching/binary_search_insertion.kt b/zh-hant/codes/kotlin/chapter_searching/binary_search_insertion.kt index 366d83604..0a2071c5f 100644 --- a/zh-hant/codes/kotlin/chapter_searching/binary_search_insertion.kt +++ b/zh-hant/codes/kotlin/chapter_searching/binary_search_insertion.kt @@ -35,7 +35,7 @@ fun binarySearchInsertion(nums: IntArray, target: Int): Int { } else if (nums[m] > target) { j = m - 1 // target 在區間 [i, m-1] 中 } else { - j = m - 1 // 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1 // 最右一個小於 target 的元素在區間 [i, m-1] 中 } } // 返回插入點 i @@ -62,4 +62,4 @@ fun main() { val index = binarySearchInsertion(nums, target) println("元素 $target 的插入點的索引為 $index") } -} \ No newline at end of file +} diff --git a/zh-hant/codes/python/chapter_searching/binary_search_insertion.py b/zh-hant/codes/python/chapter_searching/binary_search_insertion.py index f793e53b1..92f72430f 100644 --- a/zh-hant/codes/python/chapter_searching/binary_search_insertion.py +++ b/zh-hant/codes/python/chapter_searching/binary_search_insertion.py @@ -30,7 +30,7 @@ def binary_search_insertion(nums: list[int], target: int) -> int: elif nums[m] > target: j = m - 1 # target 在區間 [i, m-1] 中 else: - j = m - 1 # 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1 # 最右一個小於 target 的元素在區間 [i, m-1] 中 # 返回插入點 i return i diff --git a/zh-hant/codes/ruby/chapter_backtracking/permutations_ii.rb b/zh-hant/codes/ruby/chapter_backtracking/permutations_ii.rb index 682fc7162..1f18a6f7d 100644 --- a/zh-hant/codes/ruby/chapter_backtracking/permutations_ii.rb +++ b/zh-hant/codes/ruby/chapter_backtracking/permutations_ii.rb @@ -4,6 +4,8 @@ Created Time: 2024-05-22 Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) =end +require 'set' + ### 回溯演算法:全排列 II ### def backtrack(state, choices, selected, res) # 當狀態長度等於元素數量時,記錄解 diff --git a/zh-hant/codes/ruby/chapter_searching/binary_search_insertion.rb b/zh-hant/codes/ruby/chapter_searching/binary_search_insertion.rb index 7928c5a26..dd275bc3f 100644 --- a/zh-hant/codes/ruby/chapter_searching/binary_search_insertion.rb +++ b/zh-hant/codes/ruby/chapter_searching/binary_search_insertion.rb @@ -39,7 +39,7 @@ def binary_search_insertion(nums, target) elsif nums[m] > target j = m - 1 # target 在區間 [i, m-1] 中 else - j = m - 1 # 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1 # 最右一個小於 target 的元素在區間 [i, m-1] 中 end end diff --git a/zh-hant/codes/rust/chapter_searching/binary_search_insertion.rs b/zh-hant/codes/rust/chapter_searching/binary_search_insertion.rs index 3a6fc63f5..bf3e0d805 100644 --- a/zh-hant/codes/rust/chapter_searching/binary_search_insertion.rs +++ b/zh-hant/codes/rust/chapter_searching/binary_search_insertion.rs @@ -32,7 +32,7 @@ pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 { } else if nums[m as usize] > target { j = m - 1; // target 在區間 [i, m-1] 中 } else { - j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中 } } // 返回插入點 i diff --git a/zh-hant/codes/swift/chapter_searching/binary_search_insertion.swift b/zh-hant/codes/swift/chapter_searching/binary_search_insertion.swift index 49f2ce267..9bb94007d 100644 --- a/zh-hant/codes/swift/chapter_searching/binary_search_insertion.swift +++ b/zh-hant/codes/swift/chapter_searching/binary_search_insertion.swift @@ -35,7 +35,7 @@ public func binarySearchInsertion(nums: [Int], target: Int) -> Int { } else if nums[m] > target { j = m - 1 // target 在區間 [i, m-1] 中 } else { - j = m - 1 // 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1 // 最右一個小於 target 的元素在區間 [i, m-1] 中 } } // 返回插入點 i diff --git a/zh-hant/codes/swift/chapter_searching/binary_search_insertion_target.swift b/zh-hant/codes/swift/chapter_searching/binary_search_insertion_target.swift index 49f2ce267..9bb94007d 100644 --- a/zh-hant/codes/swift/chapter_searching/binary_search_insertion_target.swift +++ b/zh-hant/codes/swift/chapter_searching/binary_search_insertion_target.swift @@ -35,7 +35,7 @@ public func binarySearchInsertion(nums: [Int], target: Int) -> Int { } else if nums[m] > target { j = m - 1 // target 在區間 [i, m-1] 中 } else { - j = m - 1 // 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1 // 最右一個小於 target 的元素在區間 [i, m-1] 中 } } // 返回插入點 i diff --git a/zh-hant/codes/swift/chapter_stack_and_queue/queue.swift b/zh-hant/codes/swift/chapter_stack_and_queue/queue.swift index 54d9d076a..32098dba1 100644 --- a/zh-hant/codes/swift/chapter_stack_and_queue/queue.swift +++ b/zh-hant/codes/swift/chapter_stack_and_queue/queue.swift @@ -26,8 +26,8 @@ enum Queue { /* 元素出列 */ // 使用 Array 模擬時 pop 的複雜度為 O(n) - let pool = queue.removeFirst() - print("出列元素 pop = \(pool),出列後 queue = \(queue)") + let pop = queue.removeFirst() + print("出列元素 pop = \(pop),出列後 queue = \(queue)") /* 獲取佇列的長度 */ let size = queue.count diff --git a/zh-hant/codes/typescript/chapter_searching/binary_search_insertion.ts b/zh-hant/codes/typescript/chapter_searching/binary_search_insertion.ts index e42bedc25..a051979d2 100644 --- a/zh-hant/codes/typescript/chapter_searching/binary_search_insertion.ts +++ b/zh-hant/codes/typescript/chapter_searching/binary_search_insertion.ts @@ -36,7 +36,7 @@ function binarySearchInsertion(nums: Array, target: number): number { } else if (nums[m] > target) { j = m - 1; // target 在區間 [i, m-1] 中 } else { - j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中 + j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中 } } // 返回插入點 i diff --git a/zh-hant/docs/chapter_appendix/installation.md b/zh-hant/docs/chapter_appendix/installation.md index c6144ee86..e35d07110 100644 --- a/zh-hant/docs/chapter_appendix/installation.md +++ b/zh-hant/docs/chapter_appendix/installation.md @@ -20,7 +20,7 @@ VS Code 擁有強大的擴展包生態系統,支持大多數程式語言的執 ### C/C++ 環境 -1. Windows 系統需要安裝 [MinGW](https://sourceforge.net/projects/mingw-w64/files/)([配置教程](https://blog.csdn.net/qq_33698226/article/details/129031241));MacOS 自帶 Clang ,無須安裝。 +1. Windows 系統需要安裝 [MinGW](https://sourceforge.net/projects/mingw-w64/files/)([配置教程](https://blog.csdn.net/qq_33698226/article/details/129031241));macOS 自帶 Clang ,無須安裝。 2. 在 VS Code 的擴充功能市場中搜索 `c++` ,安裝 C/C++ Extension Pack 。 3. (可選)開啟 Settings 頁面,搜尋 `Clang_format_fallback Style` 程式碼格式化選項,設定為 `{ BasedOnStyle: Microsoft, BreakBeforeBraces: Attach }` 。 @@ -31,7 +31,7 @@ VS Code 擁有強大的擴展包生態系統,支持大多數程式語言的執 ### C# 環境 -1. 下載並安裝 [.Net 8.0](https://dotnet.microsoft.com/en-us/download) 。 +1. 下載並安裝 [.NET 8.0](https://dotnet.microsoft.com/en-us/download) 。 2. 在 VS Code 的擴充功能市場中搜索 `C# Dev Kit` ,安裝 C# Dev Kit ([配置教程](https://code.visualstudio.com/docs/csharp/get-started))。 3. 也可使用 Visual Studio([安裝教程](https://learn.microsoft.com/zh-cn/visualstudio/install/install-visual-studio?view=vs-2022))。 diff --git a/zh-hant/docs/chapter_appendix/terminology.md b/zh-hant/docs/chapter_appendix/terminology.md index 88d8a5a43..c27c57c30 100644 --- a/zh-hant/docs/chapter_appendix/terminology.md +++ b/zh-hant/docs/chapter_appendix/terminology.md @@ -80,7 +80,9 @@ | level-order traversal | 層序走訪 | | breadth-first traversal | 廣度優先走訪 | | depth-first traversal | 深度優先走訪 | -| binary search tree | 二元搜尋樹 | +| pre-order traversal | 前序走訪 | +| in-order traversal | 中序走訪 | +| post-order traversal | 後序走訪 | | balanced binary search tree | 平衡二元搜尋樹 | | balance factor | 平衡因子 | | heap | 堆積 | diff --git a/zh-hant/docs/chapter_array_and_linkedlist/linked_list.md b/zh-hant/docs/chapter_array_and_linkedlist/linked_list.md index 40d5bc6a7..fdfb96c3e 100755 --- a/zh-hant/docs/chapter_array_and_linkedlist/linked_list.md +++ b/zh-hant/docs/chapter_array_and_linkedlist/linked_list.md @@ -166,7 +166,7 @@ // 建構子 class ListNode(x: Int) { val _val: Int = x // 節點值 - val next: ListNode? = null // 指向下一個節點的引用 + var next: ListNode? = null // 指向下一個節點的引用 } ``` @@ -658,8 +658,8 @@ // 建構子 class ListNode(x: Int) { val _val: Int = x // 節點值 - val next: ListNode? = null // 指向後繼節點的引用 - val prev: ListNode? = null // 指向前驅節點的引用 + var next: ListNode? = null // 指向後繼節點的引用 + var prev: ListNode? = null // 指向前驅節點的引用 } ``` diff --git a/zh-hant/docs/chapter_backtracking/permutations_problem.md b/zh-hant/docs/chapter_backtracking/permutations_problem.md index 58fac34f7..3165b8ac4 100644 --- a/zh-hant/docs/chapter_backtracking/permutations_problem.md +++ b/zh-hant/docs/chapter_backtracking/permutations_problem.md @@ -30,7 +30,7 @@ 為了實現每個元素只被選擇一次,我們考慮引入一個布林型陣列 `selected` ,其中 `selected[i]` 表示 `choices[i]` 是否已被選擇,並基於它實現以下剪枝操作。 -- 在做出選擇 `choice[i]` 後,我們就將 `selected[i]` 賦值為 $\text{True}$ ,代表它已被選擇。 +- 在做出選擇 `choices[i]` 後,我們就將 `selected[i]` 賦值為 $\text{True}$ ,代表它已被選擇。 - 走訪選擇串列 `choices` 時,跳過所有已被選擇的節點,即剪枝。 如下圖所示,假設我們第一輪選擇 1 ,第二輪選擇 3 ,第三輪選擇 2 ,則需要在第二輪剪掉元素 1 的分支,在第三輪剪掉元素 1 和元素 3 的分支。 diff --git a/zh-hant/docs/chapter_data_structure/summary.md b/zh-hant/docs/chapter_data_structure/summary.md index 415b2d61d..eb53ed93a 100644 --- a/zh-hant/docs/chapter_data_structure/summary.md +++ b/zh-hant/docs/chapter_data_structure/summary.md @@ -10,7 +10,7 @@ - 原碼、一補數和二補數是在計算機中編碼數字的三種方法,它們之間可以相互轉換。整數的原碼的最高位是符號位,其餘位是數字的值。 - 整數在計算機中是以二補數的形式儲存的。在二補數表示下,計算機可以對正數和負數的加法一視同仁,不需要為減法操作單獨設計特殊的硬體電路,並且不存在正負零歧義的問題。 - 浮點數的編碼由 1 位符號位、8 位指數位和 23 位分數位構成。由於存在指數位,因此浮點數的取值範圍遠大於整數,代價是犧牲了精度。 -- ASCII 碼是最早出現的英文字元集,長度為 1 位元組,共收錄 127 個字元。GBK 字元集是常用的中文字元集,共收錄兩萬多個漢字。Unicode 致力於提供一個完整的字元集標準,收錄世界上各種語言的字元,從而解決由於字元編碼方法不一致而導致的亂碼問題。 +- ASCII 碼是最早出現的英文字元集,長度為 1 位元組,共收錄 128 個字元。GBK 字元集是常用的中文字元集,共收錄兩萬多個漢字。Unicode 致力於提供一個完整的字元集標準,收錄世界上各種語言的字元,從而解決由於字元編碼方法不一致而導致的亂碼問題。 - UTF-8 是最受歡迎的 Unicode 編碼方法,通用性非常好。它是一種變長的編碼方法,具有很好的擴展性,有效提升了儲存空間的使用效率。UTF-16 和 UTF-32 是等長的編碼方法。在編碼中文時,UTF-16 佔用的空間比 UTF-8 更小。Java 和 C# 等程式語言預設使用 UTF-16 編碼。 ### Q & A diff --git a/zh-hant/docs/chapter_divide_and_conquer/build_binary_tree_problem.md b/zh-hant/docs/chapter_divide_and_conquer/build_binary_tree_problem.md index 88917b9bd..760757943 100644 --- a/zh-hant/docs/chapter_divide_and_conquer/build_binary_tree_problem.md +++ b/zh-hant/docs/chapter_divide_and_conquer/build_binary_tree_problem.md @@ -21,12 +21,12 @@ 根據定義,`preorder` 和 `inorder` 都可以劃分為三個部分。 - 前序走訪:`[ 根節點 | 左子樹 | 右子樹 ]` ,例如上圖的樹對應 `[ 3 | 9 | 2 1 7 ]` 。 -- 中序走訪:`[ 左子樹 | 根節點 | 右子樹 ]` ,例如上圖的樹對應 `[ 9 | 3 | 1 2 7 ]` 。 +- 中序走訪:`[ 左子樹 | 根節點 | 右子樹 ]` ,例如上圖的樹對應 `[ 9 | 3 | 1 2 7 ]` 。 以上圖資料為例,我們可以透過下圖所示的步驟得到劃分結果。 1. 前序走訪的首元素 3 是根節點的值。 -2. 查詢根節點 3 在 `inorder` 中的索引,利用該索引可將 `inorder` 劃分為 `[ 9 | 3 | 1 2 7 ]` 。 +2. 查詢根節點 3 在 `inorder` 中的索引,利用該索引可將 `inorder` 劃分為 `[ 9 | 3 | 1 2 7 ]` 。 3. 根據 `inorder` 的劃分結果,易得左子樹和右子樹的節點數量分別為 1 和 3 ,從而可將 `preorder` 劃分為 `[ 3 | 9 | 2 1 7 ]` 。 ![在前序走訪和中序走訪中劃分子樹](build_binary_tree_problem.assets/build_tree_preorder_inorder_division.png) diff --git a/zh-hant/docs/chapter_dynamic_programming/dp_problem_features.md b/zh-hant/docs/chapter_dynamic_programming/dp_problem_features.md index feda346f2..5948ad479 100644 --- a/zh-hant/docs/chapter_dynamic_programming/dp_problem_features.md +++ b/zh-hant/docs/chapter_dynamic_programming/dp_problem_features.md @@ -6,7 +6,7 @@ - 動態規劃也對問題進行遞迴分解,但與分治演算法的主要區別是,動態規劃中的子問題是相互依賴的,在分解過程中會出現許多重疊子問題。 - 回溯演算法在嘗試和回退中窮舉所有可能的解,並透過剪枝避免不必要的搜尋分支。原問題的解由一系列決策步驟構成,我們可以將每個決策步驟之前的子序列看作一個子問題。 -實際上,動態規劃常用來求解最最佳化問題,它們不僅包含重疊子問題,還具有另外兩大特性:最優子結構、無後效性。 +實際上,動態規劃常用來求解最佳化問題,它們不僅包含重疊子問題,還具有另外兩大特性:最優子結構、無後效性。 ## 最優子結構 diff --git a/zh-hant/docs/chapter_dynamic_programming/dp_solution_pipeline.md b/zh-hant/docs/chapter_dynamic_programming/dp_solution_pipeline.md index 13026aabd..b90cfe7d9 100644 --- a/zh-hant/docs/chapter_dynamic_programming/dp_solution_pipeline.md +++ b/zh-hant/docs/chapter_dynamic_programming/dp_solution_pipeline.md @@ -15,7 +15,7 @@ 在此基礎上,動態規劃問題還有一些判斷的“加分項”。 -- 問題包含最大(小)或最多(少)等最最佳化描述。 +- 問題包含最大(小)或最多(少)等最佳化描述。 - 問題的狀態能夠使用一個串列、多維矩陣或樹來表示,並且一個狀態與其周圍的狀態存在遞推關係。 相應地,也存在一些“減分項”。 diff --git a/zh-hant/docs/chapter_dynamic_programming/edit_distance_problem.md b/zh-hant/docs/chapter_dynamic_programming/edit_distance_problem.md index d8d8e407a..9579fe22b 100644 --- a/zh-hant/docs/chapter_dynamic_programming/edit_distance_problem.md +++ b/zh-hant/docs/chapter_dynamic_programming/edit_distance_problem.md @@ -35,7 +35,7 @@ 狀態 $[i, j]$ 對應的子問題:**將 $s$ 的前 $i$ 個字元更改為 $t$ 的前 $j$ 個字元所需的最少編輯步數**。 -至此,得到一個尺寸為 $(i+1) \times (j+1)$ 的二維 $dp$ 表。 +至此,得到一個尺寸為 $(n+1) \times (m+1)$ 的二維 $dp$ 表。 **第二步:找出最優子結構,進而推導出狀態轉移方程** diff --git a/zh-hant/docs/chapter_dynamic_programming/knapsack_problem.md b/zh-hant/docs/chapter_dynamic_programming/knapsack_problem.md index bb675fac1..1d2476869 100644 --- a/zh-hant/docs/chapter_dynamic_programming/knapsack_problem.md +++ b/zh-hant/docs/chapter_dynamic_programming/knapsack_problem.md @@ -134,7 +134,7 @@ $$ ### 空間最佳化 -由於每個狀態都只與其上一行的狀態有關,因此我們可以使用兩個陣列滾動前進,將空間複雜度從 $O(n^2)$ 降至 $O(n)$ 。 +由於每個狀態都只與其上一行的狀態有關,因此我們可以使用兩個陣列滾動前進,將空間複雜度從 $O(n \times cap)$ 降至 $O(cap)$ 。 進一步思考,我們能否僅用一個陣列實現空間最佳化呢?觀察可知,每個狀態都是由正上方或左上方的格子轉移過來的。假設只有一個陣列,當開始走訪第 $i$ 行時,該陣列儲存的仍然是第 $i-1$ 行的狀態。 diff --git a/zh-hant/docs/chapter_dynamic_programming/summary.md b/zh-hant/docs/chapter_dynamic_programming/summary.md index ae025eed1..2258faeb1 100644 --- a/zh-hant/docs/chapter_dynamic_programming/summary.md +++ b/zh-hant/docs/chapter_dynamic_programming/summary.md @@ -21,5 +21,5 @@ **編輯距離問題** - 編輯距離(Levenshtein 距離)用於衡量兩個字串之間的相似度,其定義為從一個字串到另一個字串的最少編輯步數,編輯操作包括新增、刪除、替換。 -- 編輯距離問題的狀態定義為將 $s$ 的前 $i$ 個字元更改為 $t$ 的前 $j$ 個字元所需的最少編輯步數。當 $s[i] \ne t[j]$ 時,具有三種決策:新增、刪除、替換,它們都有相應的剩餘子問題。據此便可以找出最優子結構與構建狀態轉移方程。而當 $s[i] = t[j]$ 時,無須編輯當前字元。 +- 編輯距離問題的狀態定義為將 $s$ 的前 $i$ 個字元更改為 $t$ 的前 $j$ 個字元所需的最少編輯步數。當 $s[i-1] \ne t[j-1]$ 時,具有三種決策:新增、刪除、替換,它們都有相應的剩餘子問題。據此便可以找出最優子結構與構建狀態轉移方程。而當 $s[i-1] = t[j-1]$ 時,無須編輯當前字元。 - 在編輯距離中,狀態依賴其正上方、正左方、左上方的狀態,因此空間最佳化後正序或倒序走訪都無法正確地進行狀態轉移。為此,我們利用一個變數暫存左上方狀態,從而轉化到與完全背包問題等價的情況,可以在空間最佳化後進行正序走訪。 diff --git a/zh-hant/docs/chapter_greedy/fractional_knapsack_problem.md b/zh-hant/docs/chapter_greedy/fractional_knapsack_problem.md index 5c4f8f5f3..02e6a8704 100644 --- a/zh-hant/docs/chapter_greedy/fractional_knapsack_problem.md +++ b/zh-hant/docs/chapter_greedy/fractional_knapsack_problem.md @@ -33,7 +33,7 @@ [file]{fractional_knapsack}-[class]{}-[func]{fractional_knapsack} ``` -內建排序演算法的時間複雜度通常為 $O(\log n)$ ,空間複雜度通常為 $O(\log n)$ 或 $O(n)$ ,取決於程式語言的具體實現。 +內建排序演算法的時間複雜度通常為 $O(n \log n)$ ,空間複雜度通常為 $O(\log n)$ 或 $O(n)$ ,取決於程式語言的具體實現。 除排序之外,在最差情況下,需要走訪整個物品串列,**因此時間複雜度為 $O(n)$** ,其中 $n$ 為物品數量。 diff --git a/zh-hant/docs/chapter_greedy/greedy_algorithm.md b/zh-hant/docs/chapter_greedy/greedy_algorithm.md index f4a5a349f..27a60ec0c 100644 --- a/zh-hant/docs/chapter_greedy/greedy_algorithm.md +++ b/zh-hant/docs/chapter_greedy/greedy_algorithm.md @@ -91,4 +91,4 @@ - **分數背包問題**:給定一組物品和一個載重量,你的目標是選擇一組物品,使得總重量不超過載重量,且總價值最大。如果每次都選擇價效比最高(價值 / 重量)的物品,那麼貪婪演算法在一些情況下可以得到最優解。 - **股票買賣問題**:給定一組股票的歷史價格,你可以進行多次買賣,但如果你已經持有股票,那麼在賣出之前不能再買,目標是獲取最大利潤。 - **霍夫曼編碼**:霍夫曼編碼是一種用於無損資料壓縮的貪婪演算法。透過構建霍夫曼樹,每次選擇出現頻率最低的兩個節點合併,最後得到的霍夫曼樹的帶權路徑長度(編碼長度)最小。 -- **Dijkstra 演算法**:它是一種解決給定源頂點到其餘各頂點的最短路徑問題的貪婪演算法。 +- **Dijkstra 演算法**:在所有邊的權重均為非負數的圖中,它是一種解決給定源頂點到其餘各頂點的最短路徑問題的貪婪演算法。 diff --git a/zh-hant/docs/chapter_greedy/max_product_cutting_problem.md b/zh-hant/docs/chapter_greedy/max_product_cutting_problem.md index c756eae85..78f67093b 100644 --- a/zh-hant/docs/chapter_greedy/max_product_cutting_problem.md +++ b/zh-hant/docs/chapter_greedy/max_product_cutting_problem.md @@ -51,7 +51,7 @@ $$ 1. 輸入整數 $n$ ,從其不斷地切分出因子 $3$ ,直至餘數為 $0$、$1$、$2$ 。 2. 當餘數為 $0$ 時,代表 $n$ 是 $3$ 的倍數,因此不做任何處理。 3. 當餘數為 $2$ 時,不繼續劃分,保留。 -4. 當餘數為 $1$ 時,由於 $2 \times 2 > 1 \times 3$ ,因此應將最後一個 $3$ 替換為 $2$ 。 +4. 當餘數為 $1$ 時,由於 $2 \times 2 > 1 \times 3$ ,因此應將最後一個 $3$ 和餘數 $1$ 替換為兩個 $2$ 。 ### 程式碼實現 diff --git a/zh-hant/docs/chapter_greedy/summary.md b/zh-hant/docs/chapter_greedy/summary.md index b09b7756d..ce79a6b9e 100644 --- a/zh-hant/docs/chapter_greedy/summary.md +++ b/zh-hant/docs/chapter_greedy/summary.md @@ -2,7 +2,7 @@ ### 重點回顧 -- 貪婪演算法通常用於解決最最佳化問題,其原理是在每個決策階段都做出區域性最優的決策,以期獲得全域性最優解。 +- 貪婪演算法通常用於解決最佳化問題,其原理是在每個決策階段都做出區域性最優的決策,以期獲得全域性最優解。 - 貪婪演算法會迭代地做出一個又一個的貪婪選擇,每輪都將問題轉化成一個規模更小的子問題,直到問題被解決。 - 貪婪演算法不僅實現簡單,還具有很高的解題效率。相比於動態規劃,貪婪演算法的時間複雜度通常更低。 - 在零錢兌換問題中,對於某些硬幣組合,貪婪演算法可以保證找到最優解;對於另外一些硬幣組合則不然,貪婪演算法可能找到很差的解。 diff --git a/zh-hant/docs/chapter_hashing/hash_algorithm.md b/zh-hant/docs/chapter_hashing/hash_algorithm.md index 8745b080e..70292667c 100644 --- a/zh-hant/docs/chapter_hashing/hash_algorithm.md +++ b/zh-hant/docs/chapter_hashing/hash_algorithm.md @@ -160,7 +160,7 @@ $$ size_t hashStr = hash()(str); // 字串“Hello 演算法”的雜湊值為 15466937326284535026 - // 在 C++ 中,內建 std:hash() 僅提供基本資料型別的雜湊值計算 + // 在 C++ 中,內建 std::hash() 僅提供基本資料型別的雜湊值計算 // 陣列、物件的雜湊值計算需要自行實現 ``` diff --git a/zh-hant/docs/chapter_hashing/summary.md b/zh-hant/docs/chapter_hashing/summary.md index eb373ac3e..e67f82728 100644 --- a/zh-hant/docs/chapter_hashing/summary.md +++ b/zh-hant/docs/chapter_hashing/summary.md @@ -10,7 +10,7 @@ - 負載因子定義為雜湊表中元素數量除以桶數量,反映了雜湊衝突的嚴重程度,常用作觸發雜湊表擴容的條件。 - 鏈式位址透過將單個元素轉化為鏈結串列,將所有衝突元素儲存在同一個鏈結串列中。然而,鏈結串列過長會降低查詢效率,可以透過進一步將鏈結串列轉換為紅黑樹來提高效率。 - 開放定址透過多次探測來處理雜湊衝突。線性探查使用固定步長,缺點是不能刪除元素,且容易產生聚集。多次雜湊使用多個雜湊函式進行探測,相較線性探查更不易產生聚集,但多個雜湊函式增加了計算量。 -- 不同程式語言採取了不同的雜湊表實現。例如,Java 的 `HashMap` 使用鏈式位址,而 Python 的 `Dict` 採用開放定址。 +- 不同程式語言採取了不同的雜湊表實現。例如,Java 的 `HashMap` 使用鏈式位址,而 Python 的 `dict` 採用開放定址。 - 在雜湊表中,我們希望雜湊演算法具有確定性、高效率和均勻分佈的特點。在密碼學中,雜湊演算法還應該具備抗碰撞性和雪崩效應。 - 雜湊演算法通常採用大質數作為模數,以最大化地保證雜湊值均勻分佈,減少雜湊衝突。 - 常見的雜湊演算法包括 MD5、SHA-1、SHA-2 和 SHA-3 等。MD5 常用於校驗檔案完整性,SHA-2 常用於安全應用與協議。 diff --git a/zh-hant/docs/chapter_heap/build_heap.md b/zh-hant/docs/chapter_heap/build_heap.md index 5b6c51049..3adcb5832 100644 --- a/zh-hant/docs/chapter_heap/build_heap.md +++ b/zh-hant/docs/chapter_heap/build_heap.md @@ -31,7 +31,7 @@ 下面,我們來嘗試推算第二種建堆積方法的時間複雜度。 -- 假設完全二元樹的節點數量為 $n$ ,則葉節點數量為 $(n + 1) / 2$ ,其中 $/$ 為向下整除。因此需要堆積化的節點數量為 $(n - 1) / 2$ 。 +- 假設完全二元樹的節點數量為 $n$ ,則葉節點數量為 $(n + 1) / 2$ ,其中 $/$ 為向下整除。因此需要堆積化的節點數量為 $n / 2$ 。 - 在從頂至底堆積化的過程中,每個節點最多堆積化到葉節點,因此最大迭代次數為二元樹高度 $\log n$ 。 將上述兩者相乘,可得到建堆積過程的時間複雜度為 $O(n \log n)$ 。**但這個估算結果並不準確,因為我們沒有考慮到二元樹底層節點數量遠多於頂層節點的性質**。 diff --git a/zh-hant/docs/chapter_searching/binary_search_edge.md b/zh-hant/docs/chapter_searching/binary_search_edge.md index b015e0cef..7799a1bed 100644 --- a/zh-hant/docs/chapter_searching/binary_search_edge.md +++ b/zh-hant/docs/chapter_searching/binary_search_edge.md @@ -41,7 +41,7 @@ ### 轉化為查詢元素 -我們知道,當陣列不包含 `target` 時,最終 $i$ 和 $j$ 會分別指向首個大於、小於 `target` 的元素。 +我們知道,當陣列不包含 `target` 時,最終 $i$ 和 $j$ 會分別指向首個大於 `target` 的元素和最右一個小於 `target` 的元素。 因此,如下圖所示,我們可以構造一個陣列中不存在的元素,用於查詢左右邊界。 diff --git a/zh-hant/docs/chapter_searching/binary_search_insertion.md b/zh-hant/docs/chapter_searching/binary_search_insertion.md index bfc0d5181..8543108b2 100644 --- a/zh-hant/docs/chapter_searching/binary_search_insertion.md +++ b/zh-hant/docs/chapter_searching/binary_search_insertion.md @@ -20,7 +20,7 @@ 進一步思考二分搜尋過程:當 `nums[m] < target` 時 $i$ 移動,這意味著指標 $i$ 在向大於等於 `target` 的元素靠近。同理,指標 $j$ 始終在向小於等於 `target` 的元素靠近。 -因此二分結束時一定有:$i$ 指向首個大於 `target` 的元素,$j$ 指向首個小於 `target` 的元素。**易得當陣列不包含 `target` 時,插入索引為 $i$** 。程式碼如下所示: +因此二分結束時一定有:$i$ 指向首個大於 `target` 的元素,$j$ 指向最右一個小於 `target` 的元素。**易得當陣列不包含 `target` 時,插入索引為 $i$** 。程式碼如下所示: ```src [file]{binary_search_insertion}-[class]{}-[func]{binary_search_insertion_simple} @@ -48,7 +48,7 @@ - 當 `nums[m] < target` 或 `nums[m] > target` 時,說明還沒有找到 `target` ,因此採用普通二分搜尋的縮小區間操作,**從而使指標 $i$ 和 $j$ 向 `target` 靠近**。 - 當 `nums[m] == target` 時,說明小於 `target` 的元素在區間 $[i, m - 1]$ 中,因此採用 $j = m - 1$ 來縮小區間,**從而使指標 $j$ 向小於 `target` 的元素靠近**。 -迴圈完成後,$i$ 指向最左邊的 `target` ,$j$ 指向首個小於 `target` 的元素,**因此索引 $i$ 就是插入點**。 +迴圈完成後,$i$ 指向最左邊的 `target` ,$j$ 指向最右一個小於 `target` 的元素,**因此索引 $i$ 就是插入點**。 === "<1>" ![二分搜尋重複元素的插入點的步驟](binary_search_insertion.assets/binary_search_insertion_step1.png) diff --git a/zh-hant/docs/chapter_sorting/bucket_sort.md b/zh-hant/docs/chapter_sorting/bucket_sort.md index e38cb4985..a4c172240 100644 --- a/zh-hant/docs/chapter_sorting/bucket_sort.md +++ b/zh-hant/docs/chapter_sorting/bucket_sort.md @@ -1,6 +1,6 @@ # 桶排序 -前述幾種排序演算法都屬於“基於比較的排序演算法”,它們透過比較元素間的大小來實現排序。此類排序演算法的時間複雜度無法超越 $O(n \log n)$ 。接下來,我們將探討幾種“非比較排序演算法”,它們的時間複雜度可以達到線性階。 +前述幾種排序演算法都屬於“基於比較的排序演算法”,它們透過比較元素間的大小來實現排序。此類排序演算法在最壞情況下的時間複雜度下界為 $\Omega(n \log n)$ 。接下來,我們將探討幾種“非比較排序演算法”,它們的時間複雜度可以達到線性階。 桶排序(bucket sort)是分治策略的一個典型應用。它透過設定一些具有大小順序的桶,每個桶對應一個數據範圍,將資料平均分配到各個桶中;然後,在每個桶內部分別執行排序;最終按照桶的順序將所有資料合併。 diff --git a/zh-hant/docs/chapter_sorting/selection_sort.md b/zh-hant/docs/chapter_sorting/selection_sort.md index 911a9b476..be69efc2c 100644 --- a/zh-hant/docs/chapter_sorting/selection_sort.md +++ b/zh-hant/docs/chapter_sorting/selection_sort.md @@ -51,7 +51,7 @@ ## 演算法特性 -- **時間複雜度為 $O(n^2)$、非自適應排序**:外迴圈共 $n - 1$ 輪,第一輪的未排序區間長度為 $n$ ,最後一輪的未排序區間長度為 $2$ ,即各輪外迴圈分別包含 $n$、$n - 1$、$\dots$、$3$、$2$ 輪內迴圈,求和為 $\frac{(n - 1)(n + 2)}{2}$ 。 +- **時間複雜度為 $O(n^2)$、非自適應排序**:外迴圈共 $n - 1$ 輪,第一輪內迴圈執行 $n - 1$ 次,最後一輪執行 $1$ 次,即各輪內迴圈分別執行 $n - 1$、$n - 2$、$\dots$、$2$、$1$ 次,求和為 $\frac{n(n - 1)}{2}$ 。 - **空間複雜度為 $O(1)$、原地排序**:指標 $i$ 和 $j$ 使用常數大小的額外空間。 - **非穩定排序**:如下圖所示,元素 `nums[i]` 有可能被交換至與其相等的元素的右邊,導致兩者的相對順序發生改變。 diff --git a/zh-hant/docs/chapter_sorting/sorting_algorithm.md b/zh-hant/docs/chapter_sorting/sorting_algorithm.md index 5e292648f..dac2018df 100644 --- a/zh-hant/docs/chapter_sorting/sorting_algorithm.md +++ b/zh-hant/docs/chapter_sorting/sorting_algorithm.md @@ -37,7 +37,7 @@ **自適應性**:自適應排序能夠利用輸入資料已有的順序資訊來減少計算量,達到更優的時間效率。自適應排序演算法的最佳時間複雜度通常優於平均時間複雜度。 -**是否基於比較**:基於比較的排序依賴比較運算子($<$、$=$、$>$)來判斷元素的相對順序,從而排序整個陣列,理論最優時間複雜度為 $O(n \log n)$ 。而非比較排序不使用比較運算子,時間複雜度可達 $O(n)$ ,但其通用性相對較差。 +**是否基於比較**:基於比較的排序依賴比較運算子($<$、$=$、$>$)來判斷元素的相對順序,從而排序整個陣列,其最壞時間複雜度的下界為 $\Omega(n \log n)$ 。而非比較排序不使用比較運算子,時間複雜度可達 $O(n)$ ,但其通用性相對較差。 ## 理想排序演算法 diff --git a/zh-hant/docs/chapter_stack_and_queue/queue.md b/zh-hant/docs/chapter_stack_and_queue/queue.md index b4fef52d2..cc1788c93 100755 --- a/zh-hant/docs/chapter_stack_and_queue/queue.md +++ b/zh-hant/docs/chapter_stack_and_queue/queue.md @@ -175,7 +175,7 @@ /* 元素出列 */ // 由於是陣列,因此 removeFirst 的複雜度為 O(n) - let pool = queue.removeFirst() + let pop = queue.removeFirst() /* 獲取佇列的長度 */ let size = queue.count @@ -244,7 +244,7 @@ ```dart title="queue.dart" /* 初始化佇列 */ - // 在 Dart 中,佇列類別 Qeque 是雙向佇列,也可作為佇列使用 + // 在 Dart 中,佇列類別 Queue 是雙向佇列,也可作為佇列使用 Queue queue = Queue(); /* 元素入列 */ diff --git a/zh-hant/docs/chapter_tree/avl_tree.md b/zh-hant/docs/chapter_tree/avl_tree.md index 532e17c2f..9fa88138b 100644 --- a/zh-hant/docs/chapter_tree/avl_tree.md +++ b/zh-hant/docs/chapter_tree/avl_tree.md @@ -205,9 +205,9 @@ AVL 樹既是二元搜尋樹,也是平衡二元樹,同時滿足這兩類二 ```kotlin title="" /* AVL 樹節點類別 */ class TreeNode(val _val: Int) { // 節點值 - val height: Int = 0 // 節點高度 - val left: TreeNode? = null // 左子節點 - val right: TreeNode? = null // 右子節點 + var height: Int = 0 // 節點高度 + var left: TreeNode? = null // 左子節點 + var right: TreeNode? = null // 右子節點 } ``` diff --git a/zh-hant/docs/chapter_tree/binary_tree.md b/zh-hant/docs/chapter_tree/binary_tree.md index c9928df87..b2ec7a2fb 100644 --- a/zh-hant/docs/chapter_tree/binary_tree.md +++ b/zh-hant/docs/chapter_tree/binary_tree.md @@ -181,8 +181,8 @@ ```kotlin title="" /* 二元樹節點類別 */ class TreeNode(val _val: Int) { // 節點值 - val left: TreeNode? = null // 左子節點引用 - val right: TreeNode? = null // 右子節點引用 + var left: TreeNode? = null // 左子節點引用 + var right: TreeNode? = null // 右子節點引用 } ```