Fix multilingual content typos (#1958)

This commit is contained in:
Yudong Jin
2026-08-18 03:08:23 +08:00
committed by GitHub
parent 69932aed18
commit bf86c39b6c
201 changed files with 289 additions and 271 deletions
@@ -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;
}
}
@@ -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
+1 -1
View File
@@ -24,6 +24,6 @@ int main() {
size_t hashStr = hash<string>()(str);
cout << "字符串 " << str << " 的哈希值为 " << hashStr << "\n";
// 在 C++ 中,内置 std:hash() 仅提供基本数据类型的哈希值计算
// 在 C++ 中,内置 std::hash() 仅提供基本数据类型的哈希值计算
// 数组、对象的哈希值计算需要自行实现
}
@@ -14,7 +14,7 @@ int binarySearchInsertion(const vector<int> &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
@@ -33,7 +33,7 @@ int binarySearchInsertion(vector<int> &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
@@ -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
@@ -31,7 +31,7 @@ int binarySearchInsertion(List<int> 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
@@ -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
}
}
@@ -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
@@ -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
@@ -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")
}
}
}
@@ -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
@@ -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)
# 当状态长度等于元素数量时,记录解
@@ -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
@@ -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
@@ -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
@@ -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
@@ -36,7 +36,7 @@ function binarySearchInsertion(nums: Array<number>, 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
+2 -2
View File
@@ -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))。
+3 -1
View File
@@ -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 | 堆 |
@@ -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 // 指向前驱节点的引用
}
```
@@ -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 的分支。
+1 -1
View File
@@ -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
@@ -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)
@@ -35,7 +35,7 @@
状态 $[i, j]$ 对应的子问题:**将 $s$ 的前 $i$ 个字符更改为 $t$ 的前 $j$ 个字符所需的最少编辑步数**。
至此,得到一个尺寸为 $(i+1) \times (j+1)$ 的二维 $dp$ 表。
至此,得到一个尺寸为 $(n+1) \times (m+1)$ 的二维 $dp$ 表。
**第二步:找出最优子结构,进而推导出状态转移方程**
@@ -134,7 +134,7 @@ $$
### 空间优化
由于每个状态都只与其上一行的状态有关,因此我们可以使用两个数组滚动前进,将空间复杂度从 $O(n^2)$ 降至 $O(n)$ 。
由于每个状态都只与其上一行的状态有关,因此我们可以使用两个数组滚动前进,将空间复杂度从 $O(n \times cap)$ 降至 $O(cap)$ 。
进一步思考,我们能否仅用一个数组实现空间优化呢?观察可知,每个状态都是由正上方或左上方的格子转移过来的。假设只有一个数组,当开始遍历第 $i$ 行时,该数组存储的仍然是第 $i-1$ 行的状态。
+1 -1
View File
@@ -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]$ 时,无须编辑当前字符。
- 在编辑距离中,状态依赖其正上方、正左方、左上方的状态,因此空间优化后正序或倒序遍历都无法正确地进行状态转移。为此,我们利用一个变量暂存左上方状态,从而转化到与完全背包问题等价的情况,可以在空间优化后进行正序遍历。
@@ -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$ 为物品数量。
+1 -1
View File
@@ -91,4 +91,4 @@
- **分数背包问题**:给定一组物品和一个载重量,你的目标是选择一组物品,使得总重量不超过载重量,且总价值最大。如果每次都选择性价比最高(价值 / 重量)的物品,那么贪心算法在一些情况下可以得到最优解。
- **股票买卖问题**:给定一组股票的历史价格,你可以进行多次买卖,但如果你已经持有股票,那么在卖出之前不能再买,目标是获取最大利润。
- **霍夫曼编码**:霍夫曼编码是一种用于无损数据压缩的贪心算法。通过构建霍夫曼树,每次选择出现频率最低的两个节点合并,最后得到的霍夫曼树的带权路径长度(编码长度)最小。
- **Dijkstra 算法**:它是一种解决给定源顶点到其余各顶点的最短路径问题的贪心算法。
- **Dijkstra 算法**在所有边的权重均为非负数的图中,它是一种解决给定源顶点到其余各顶点的最短路径问题的贪心算法。
@@ -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$ 。
### 代码实现
+1 -1
View File
@@ -160,7 +160,7 @@ $$
size_t hashStr = hash<string>()(str);
// 字符串“Hello 算法”的哈希值为 15466937326284535026
// 在 C++ 中,内置 std:hash() 仅提供基本数据类型的哈希值计算
// 在 C++ 中,内置 std::hash() 仅提供基本数据类型的哈希值计算
// 数组、对象的哈希值计算需要自行实现
```
+1 -1
View File
@@ -10,7 +10,7 @@
- 负载因子定义为哈希表中元素数量除以桶数量,反映了哈希冲突的严重程度,常用作触发哈希表扩容的条件。
- 链式地址通过将单个元素转化为链表,将所有冲突元素存储在同一个链表中。然而,链表过长会降低查询效率,可以通过进一步将链表转换为红黑树来提高效率。
- 开放寻址通过多次探测来处理哈希冲突。线性探测使用固定步长,缺点是不能删除元素,且容易产生聚集。多次哈希使用多个哈希函数进行探测,相较线性探测更不易产生聚集,但多个哈希函数增加了计算量。
- 不同编程语言采取了不同的哈希表实现。例如,Java 的 `HashMap` 使用链式地址,而 Python 的 `Dict` 采用开放寻址。
- 不同编程语言采取了不同的哈希表实现。例如,Java 的 `HashMap` 使用链式地址,而 Python 的 `dict` 采用开放寻址。
- 在哈希表中,我们希望哈希算法具有确定性、高效率和均匀分布的特点。在密码学中,哈希算法还应该具备抗碰撞性和雪崩效应。
- 哈希算法通常采用大质数作为模数,以最大化地保证哈希值均匀分布,减少哈希冲突。
- 常见的哈希算法包括 MD5、SHA-1、SHA-2 和 SHA-3 等。MD5 常用于校验文件完整性,SHA-2 常用于安全应用与协议。
+1 -1
View File
@@ -31,7 +31,7 @@
下面,我们来尝试推算第二种建堆方法的时间复杂度。
- 假设完全二叉树的节点数量为 $n$ ,则叶节点数量为 $(n + 1) / 2$ ,其中 $/$ 为向下整除。因此需要堆化的节点数量为 $(n - 1) / 2$ 。
- 假设完全二叉树的节点数量为 $n$ ,则叶节点数量为 $(n + 1) / 2$ ,其中 $/$ 为向下整除。因此需要堆化的节点数量为 $n / 2$ 。
- 在从顶至底堆化的过程中,每个节点最多堆化到叶节点,因此最大迭代次数为二叉树高度 $\log n$ 。
将上述两者相乘,可得到建堆过程的时间复杂度为 $O(n \log n)$ 。**但这个估算结果并不准确,因为我们没有考虑到二叉树底层节点数量远多于顶层节点的性质**。
+1 -1
View File
@@ -41,7 +41,7 @@
### 转化为查找元素
我们知道,当数组不包含 `target` 时,最终 $i$ 和 $j$ 会分别指向首个大于小于 `target` 的元素。
我们知道,当数组不包含 `target` 时,最终 $i$ 和 $j$ 会分别指向首个大于 `target` 的元素和最右一个小于 `target` 的元素。
因此,如下图所示,我们可以构造一个数组中不存在的元素,用于查找左右边界。
@@ -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)
+1 -1
View File
@@ -1,6 +1,6 @@
# 桶排序
前述几种排序算法都属于“基于比较的排序算法”,它们通过比较元素间的大小来实现排序。此类排序算法的时间复杂度无法超越 $O(n \log n)$ 。接下来,我们将探讨几种“非比较排序算法”,它们的时间复杂度可以达到线性阶。
前述几种排序算法都属于“基于比较的排序算法”,它们通过比较元素间的大小来实现排序。此类排序算法在最坏情况下的时间复杂度下界为 $\Omega(n \log n)$ 。接下来,我们将探讨几种“非比较排序算法”,它们的时间复杂度可以达到线性阶。
<u>桶排序(bucket sort</u>是分治策略的一个典型应用。它通过设置一些具有大小顺序的桶,每个桶对应一个数据范围,将数据平均分配到各个桶中;然后,在每个桶内部分别执行排序;最终按照桶的顺序将所有数据合并。
+1 -1
View File
@@ -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]` 有可能被交换至与其相等的元素的右边,导致两者的相对顺序发生改变。
+1 -1
View File
@@ -37,7 +37,7 @@
**自适应性**<u>自适应排序</u>能够利用输入数据已有的顺序信息来减少计算量,达到更优的时间效率。自适应排序算法的最佳时间复杂度通常优于平均时间复杂度。
**是否基于比较**<u>基于比较的排序</u>依赖比较运算符($<$、$=$、$>$)来判断元素的相对顺序,从而排序整个数组,理论最优时间复杂度为 $O(n \log n)$ 。而<u>非比较排序</u>不使用比较运算符,时间复杂度可达 $O(n)$ ,但其通用性相对较差。
**是否基于比较**<u>基于比较的排序</u>依赖比较运算符($<$、$=$、$>$)来判断元素的相对顺序,从而排序整个数组,其最坏时间复杂度的下界为 $\Omega(n \log n)$ 。而<u>非比较排序</u>不使用比较运算符,时间复杂度可达 $O(n)$ ,但其通用性相对较差。
## 理想排序算法
+2 -2
View File
@@ -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<int> queue = Queue();
/* 元素入队 */
+3 -3
View File
@@ -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 // 右子节点
}
```
+2 -2
View File
@@ -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 // 右子节点引用
}
```
@@ -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;
}
}
@@ -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
@@ -14,7 +14,7 @@ int binarySearchInsertion(const vector<int> &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
@@ -33,7 +33,7 @@ int binarySearchInsertion(vector<int> &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
@@ -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
@@ -31,7 +31,7 @@ int binarySearchInsertion(List<int> 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
@@ -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
}
}
@@ -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
@@ -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 = {
@@ -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")
}
}
}
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -36,7 +36,7 @@ function binarySearchInsertion(nums: Array<number>, 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 };
+3 -1
View File
@@ -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 |
@@ -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
}
```
@@ -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)
@@ -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**
@@ -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$.
@@ -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.
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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**.
@@ -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.
@@ -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)
+1 -1
View File
@@ -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.
<u>Bucket sort</u> 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.
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -37,7 +37,7 @@ Stable sorting is a necessary condition for multi-level sorting scenarios. Suppo
**Adaptability**: <u>Adaptive sorting</u> 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**: <u>Comparison-based sorting</u> 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)$. <u>Non-comparison sorting</u> 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**: <u>Comparison-based sorting</u> 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)$. <u>Non-comparison sorting</u> does not use comparison operators and can achieve a time complexity of $O(n)$, but its versatility is relatively limited.
## Ideal Sorting Algorithm
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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
}
```
+2 -2
View File
@@ -181,8 +181,8 @@ A <u>binary tree</u> 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
}
```
@@ -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;
}
}
@@ -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 を返す
@@ -131,4 +131,4 @@ int main() {
delArrayQueue(queue);
return 0;
}
}
@@ -14,7 +14,7 @@ int binarySearchInsertion(const vector<int> &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 を返す
@@ -33,7 +33,7 @@ int binarySearchInsertion(vector<int> &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 を返す
@@ -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 を返す
@@ -31,7 +31,7 @@ int binarySearchInsertion(List<int> 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 を返す
@@ -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
}
}
@@ -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 を返す
@@ -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 を返す
@@ -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")
}
}
}
@@ -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
@@ -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)
# 状態の長さが要素数に等しければ、解を記録
@@ -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
@@ -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 を返す
@@ -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
@@ -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
@@ -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
@@ -36,7 +36,7 @@ function binarySearchInsertion(nums: Array<number>, 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 を返す
+2 -2
View File
@@ -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))。
+3 -1
View File
@@ -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 | ヒープ |
@@ -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 // 前駆ノードへの参照
}
```
@@ -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 の分岐を刈り取る必要があります。
+1 -1
View File
@@ -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
@@ -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)

Some files were not shown because too many files have changed in this diff Show More