Skip to content

Commit

Permalink
Unify punctuation.
Browse files Browse the repository at this point in the history
  • Loading branch information
krahets committed Jul 26, 2023
1 parent 3597306 commit 63a0e73
Show file tree
Hide file tree
Showing 46 changed files with 201 additions and 201 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@

我们正在加速更新本书,欢迎您通过提交 Pull Request 来[参与本项目](https://www.hello-algo.com/chapter_appendix/contribution/),以帮助其他读者获取更优质的学习内容。

- 若您发现语法错误、内容缺失、文字歧义、无效链接、解释不清晰等问题,请协助修正或在评论区指出
- 期待您参与 C++, Python, Go, JavaScript, TypeScript, C, C#, Swift, Zig, Rust, Dart 等语言的[代码翻译](https://github.com/krahets/hello-algo/issues/15)
- 欢迎您为本书内容提出宝贵意见和建议,如有任何问题请提交 Issues 或微信联系 krahets-jyd
- 若您发现语法错误、内容缺失、文字歧义、无效链接、解释不清晰等问题,请协助修正或在评论区指出
- 期待您参与 C++, Python, Go, JavaScript, TypeScript, C, C#, Swift, Zig, Rust, Dart 等语言的[代码翻译](https://github.com/krahets/hello-algo/issues/15)
- 欢迎您为本书内容提出宝贵意见和建议,如有任何问题请提交 Issues 或微信联系 krahets-jyd

感谢本开源书的每一位撰稿人,是他们的无私奉献让这本书变得更好,他们是:

Expand Down
16 changes: 8 additions & 8 deletions docs/chapter_appendix/contribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@

在每个页面的右上角有一个「编辑」图标,您可以按照以下步骤修改文本或代码:

1. 点击编辑按钮,如果遇到“需要 Fork 此仓库”的提示,请同意该操作
2. 修改 Markdown 源文件内容,并确保内容正确,同时尽量保持排版格式的统一
1. 点击编辑按钮,如果遇到“需要 Fork 此仓库”的提示,请同意该操作
2. 修改 Markdown 源文件内容,并确保内容正确,同时尽量保持排版格式的统一
3. 在页面底部填写修改说明,然后点击“Propose file change”按钮;页面跳转后,点击“Create pull request”按钮即可发起拉取请求。

![页面编辑按键](contribution.assets/edit_markdown.png)

由于图片无法直接修改,因此需要通过新建 [Issue](https://github.com/krahets/hello-algo/issues) 或评论留言来描述图片问题,我们会尽快重新绘制并替换图片。
由于图片无法直接修改,因此需要通过新建 [Issue](https://github.com/krahets/hello-algo/issues) 或评论留言来描述问题,我们会尽快重新绘制并替换图片。

## 内容创作

如果您有兴趣参与此开源项目,包括将代码翻译成其他编程语言、扩展文章内容等,那么需要实施 Pull Request 工作流程:

1. 登录 GitHub ,将[本仓库](https://github.com/krahets/hello-algo) Fork 到个人账号下
2. 进入您的 Fork 仓库网页,使用 git clone 命令将仓库克隆至本地
3. 在本地进行内容创作,并通过运行测试以验证代码的正确性
4. 将本地所做更改 Commit ,然后 Push 至远程仓库
5. 刷新仓库网页,点击“Create pull request”按钮即可发起拉取请求
1. 登录 GitHub ,将[本仓库](https://github.com/krahets/hello-algo) Fork 到个人账号下
2. 进入您的 Fork 仓库网页,使用 git clone 命令将仓库克隆至本地
3. 在本地进行内容创作,并通过运行测试以验证代码的正确性
4. 将本地所做更改 Commit ,然后 Push 至远程仓库
5. 刷新仓库网页,点击“Create pull request”按钮即可发起拉取请求

## Docker 部署

Expand Down
6 changes: 3 additions & 3 deletions docs/chapter_appendix/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@

## C# 环境

1. 下载并安装 [.Net 6.0](https://dotnet.microsoft.com/en-us/download)
1. 下载并安装 [.Net 6.0](https://dotnet.microsoft.com/en-us/download)
2. 在 VSCode 的插件市场中搜索 `c#` ,安装 c# 。

## Swift 环境

1. 下载并安装 [Swift](https://www.swift.org/download/)
1. 下载并安装 [Swift](https://www.swift.org/download/)
2. 在 VSCode 的插件市场中搜索 `swift` ,安装 [Swift for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=sswg.swift-lang)

## Rust 环境

1. 下载并安装 [Rust](https://www.rust-lang.org/tools/install)
1. 下载并安装 [Rust](https://www.rust-lang.org/tools/install)
2. 在 VSCode 的插件市场中搜索 `rust` ,安装 [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
6 changes: 3 additions & 3 deletions docs/chapter_array_and_linkedlist/summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@

栈内存分配由编译器自动完成,而堆内存由程序员在代码中分配(注意,这里的栈和堆和数据结构中的栈和堆不是同一概念)。

1. 栈不灵活,分配的内存大小不可更改;堆相对灵活,可以动态分配内存
2. 栈是一块比较小的内存,容易出现内存不足;堆内存很大,但是由于是动态分配,容易碎片化,管理堆内存的难度更大、成本更高
3. 访问栈比访问堆更快,因为栈内存较小、对缓存友好,堆帧分散在很大的空间内,会出现更多的缓存未命中
1. 栈不灵活,分配的内存大小不可更改;堆相对灵活,可以动态分配内存
2. 栈是一块比较小的内存,容易出现内存不足;堆内存很大,但是由于是动态分配,容易碎片化,管理堆内存的难度更大、成本更高
3. 访问栈比访问堆更快,因为栈内存较小、对缓存友好,堆帧分散在很大的空间内,会出现更多的缓存未命中

!!! question "为什么数组会强调要求相同类型的元素,而在链表中却没有强调同类型呢?"

Expand Down
6 changes: 3 additions & 3 deletions docs/chapter_backtracking/backtracking_algorithm.md
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,6 @@

请注意,对于许多组合优化问题,回溯都不是最优解决方案,例如:

- 0-1 背包问题通常使用动态规划解决,以达到更高的时间效率
- 旅行商是一个著名的 NP-Hard 问题,常用解法有遗传算法和蚁群算法等
- 最大团问题是图论中的一个经典问题,可用贪心等启发式算法来解决
- 0-1 背包问题通常使用动态规划解决,以达到更高的时间效率
- 旅行商是一个著名的 NP-Hard 问题,常用解法有遗传算法和蚁群算法等
- 最大团问题是图论中的一个经典问题,可用贪心等启发式算法来解决
4 changes: 2 additions & 2 deletions docs/chapter_backtracking/subset_sum_problem.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@

分支越靠右,需要排除的分支也越多,例如:

1. 前两轮选择 $3$ , $5$ ,生成子集 $[3, 5, \cdots]$
2. 前两轮选择 $4$ , $5$ ,生成子集 $[4, 5, \cdots]$
1. 前两轮选择 $3$ , $5$ ,生成子集 $[3, 5, \cdots]$
2. 前两轮选择 $4$ , $5$ ,生成子集 $[4, 5, \cdots]$
3. 若第一轮选择 $5$ ,**则第二轮应该跳过 $3$ 和 $4$** ,因为子集 $[5, 3, \cdots]$ 和子集 $[5, 4, \cdots]$ 和 `1.` , `2.` 中生成的子集完全重复。

![不同选择顺序导致的重复子集](subset_sum_problem.assets/subset_sum_i_pruning.png)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@

**复杂度分析评估的是算法运行效率随着输入数据量增多时的增长趋势**。这个定义有些拗口,我们可以将其分为三个重点来理解:

- “算法运行效率”可分为“运行时间”和“占用空间”,因此我们可以将复杂度分为「时间复杂度 Time Complexity」和「空间复杂度 Space Complexity」
- “随着输入数据量增多时”表示复杂度与输入数据量有关,反映了算法运行效率与输入数据量之间的关系
- “增长趋势”表示复杂度分析关注的是算法时间与空间的增长趋势,而非具体的运行时间或占用空间
- “算法运行效率”可分为“运行时间”和“占用空间”,因此我们可以将复杂度分为「时间复杂度 Time Complexity」和「空间复杂度 Space Complexity」
- “随着输入数据量增多时”表示复杂度与输入数据量有关,反映了算法运行效率与输入数据量之间的关系
- “增长趋势”表示复杂度分析关注的是算法时间与空间的增长趋势,而非具体的运行时间或占用空间

**复杂度分析克服了实际测试方法的弊端**。首先,它独立于测试环境,因此分析结果适用于所有运行平台。其次,它可以体现不同数据量下的算法效率,尤其是在大数据量下的算法性能。

Expand Down
10 changes: 5 additions & 5 deletions docs/chapter_computational_complexity/space_complexity.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

算法运行过程中使用的内存空间主要包括以下几种:

- 「输入空间」用于存储算法的输入数据
- 「暂存空间」用于存储算法运行过程中的变量、对象、函数上下文等数据
- 「输出空间」用于存储算法的输出数据
- 「输入空间」用于存储算法的输入数据
- 「暂存空间」用于存储算法运行过程中的变量、对象、函数上下文等数据
- 「输出空间」用于存储算法的输出数据

通常情况下,空间复杂度统计范围是「暂存空间」+「输出空间」。

Expand Down Expand Up @@ -286,8 +286,8 @@

**最差空间复杂度中的“最差”有两层含义**,分别是输入数据的最差分布和算法运行过程中的最差时间点。

- **以最差输入数据为准**。当 $n < 10$ 时,空间复杂度为 $O(1)$ ;但当 $n > 10$ 时,初始化的数组 `nums` 占用 $O(n)$ 空间;因此最差空间复杂度为 $O(n)$
- **以算法运行过程中的峰值内存为准**。例如,程序在执行最后一行之前,占用 $O(1)$ 空间;当初始化数组 `nums` 时,程序占用 $O(n)$ 空间;因此最差空间复杂度为 $O(n)$
- **以最差输入数据为准**。当 $n < 10$ 时,空间复杂度为 $O(1)$ ;但当 $n > 10$ 时,初始化的数组 `nums` 占用 $O(n)$ 空间;因此最差空间复杂度为 $O(n)$
- **以算法运行过程中的峰值内存为准**。例如,程序在执行最后一行之前,占用 $O(1)$ 空间;当初始化数组 `nums` 时,程序占用 $O(n)$ 空间;因此最差空间复杂度为 $O(n)$

=== "Java"

Expand Down
4 changes: 2 additions & 2 deletions docs/chapter_computational_complexity/time_complexity.md
Original file line number Diff line number Diff line change
Expand Up @@ -1632,8 +1632,8 @@ $$

**某些算法的时间复杂度不是固定的,而是与输入数据的分布有关**。例如,假设输入一个长度为 $n$ 的数组 `nums` ,其中 `nums` 由从 $1$ 至 $n$ 的数字组成,但元素顺序是随机打乱的;算法的任务是返回元素 $1$ 的索引。我们可以得出以下结论:

-`nums = [?, ?, ..., 1]` ,即当末尾元素是 $1$ 时,需要完整遍历数组,此时达到 **最差时间复杂度 $O(n)$**
-`nums = [1, ?, ?, ...]` ,即当首个数字为 $1$ 时,无论数组多长都不需要继续遍历,此时达到 **最佳时间复杂度 $\Omega(1)$**
-`nums = [?, ?, ..., 1]` ,即当末尾元素是 $1$ 时,需要完整遍历数组,此时达到 **最差时间复杂度 $O(n)$**
-`nums = [1, ?, ?, ...]` ,即当首个数字为 $1$ 时,无论数组多长都不需要继续遍历,此时达到 **最佳时间复杂度 $\Omega(1)$**

“函数渐近上界”使用大 $O$ 记号表示,代表「最差时间复杂度」。相应地,“函数渐近下界”用 $\Omega$ 记号来表示,代表「最佳时间复杂度」。

Expand Down
12 changes: 6 additions & 6 deletions docs/chapter_data_structure/basic_data_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

**基本数据类型是 CPU 可以直接进行运算的类型,在算法中直接被使用**。它包括:

- 整数类型 `byte` , `short` , `int` , `long`
- 浮点数类型 `float` , `double` ,用于表示小数
- 字符类型 `char` ,用于表示各种语言的字母、标点符号、甚至表情符号等
- 布尔类型 `bool` ,用于表示“是”与“否”判断
- 整数类型 `byte` , `short` , `int` , `long`
- 浮点数类型 `float` , `double` ,用于表示小数
- 字符类型 `char` ,用于表示各种语言的字母、标点符号、甚至表情符号等
- 布尔类型 `bool` ,用于表示“是”与“否”判断

**所有基本数据类型都以二进制的形式存储在计算机中**。在计算机中,我们将 $1$ 个二进制位称为 $1$ 比特,并规定 $1$ 字节(byte)由 $8$ 比特(bits)组成。基本数据类型的取值范围取决于其占用的空间大小,例如:

- 整数类型 `byte` 占用 $1$ byte = $8$ bits ,可以表示 $2^{8}$ 个不同的数字
- 整数类型 `int` 占用 $4$ bytes = $32$ bits ,可以表示 $2^{32}$ 个数字
- 整数类型 `byte` 占用 $1$ byte = $8$ bits ,可以表示 $2^{8}$ 个不同的数字
- 整数类型 `int` 占用 $4$ bytes = $32$ bits ,可以表示 $2^{32}$ 个数字

下表列举了各种基本数据类型的占用空间、取值范围和默认值。此表格无需硬背,大致理解即可,需要时可以通过查表来回忆。

Expand Down
14 changes: 7 additions & 7 deletions docs/chapter_data_structure/classification_of_data_structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@

逻辑结构通常分为“线性”和“非线性”两类。线性结构比较直观,指数据在逻辑关系上呈线性排列;非线性结构则相反,呈非线性排列。

- **线性数据结构**:数组、链表、栈、队列、哈希表
- **非线性数据结构**:树、堆、图、哈希表
- **线性数据结构**:数组、链表、栈、队列、哈希表
- **非线性数据结构**:树、堆、图、哈希表

![线性与非线性数据结构](classification_of_data_structure.assets/classification_logic_structure.png)

非线性数据结构可以进一步被划分为树形结构和网状结构。

- **线性结构**:数组、链表、队列、栈、哈希表,元素存在一对一的顺序关系
- **树形结构**:树、堆、哈希表,元素存在一对多的关系
- **网状结构**:图,元素存在多对多的关系
- **线性结构**:数组、链表、队列、栈、哈希表,元素存在一对一的顺序关系
- **树形结构**:树、堆、哈希表,元素存在一对多的关系
- **网状结构**:图,元素存在多对多的关系

## 物理结构:连续与离散

Expand All @@ -37,8 +37,8 @@

**所有数据结构都是基于数组、链表或二者的组合实现的**。例如,栈和队列既可以使用数组实现,也可以使用链表实现;而哈希表的实现可能同时包含数组和链表。

- **基于数组可实现**:栈、队列、哈希表、树、堆、图、矩阵、张量(维度 $\geq 3$ 的数组)等
- **基于链表可实现**:栈、队列、哈希表、树、堆、图等
- **基于数组可实现**:栈、队列、哈希表、树、堆、图、矩阵、张量(维度 $\geq 3$ 的数组)等
- **基于链表可实现**:栈、队列、哈希表、树、堆、图等

基于数组实现的数据结构也被称为“静态数据结构”,这意味着此类数据结构在初始化后长度不可变。相对应地,基于链表实现的数据结构被称为“动态数据结构”,这类数据结构在初始化后,仍可以在程序运行过程中对其长度进行调整。

Expand Down
10 changes: 5 additions & 5 deletions docs/chapter_data_structure/number_encoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ $$

实际上,这是因为浮点数 `float` 采用了不同的表示方式。根据 IEEE 754 标准,32-bit 长度的 `float` 由以下部分构成:

- 符号位 $\mathrm{S}$ :占 1 bit
- 指数位 $\mathrm{E}$ :占 8 bits
- 分数位 $\mathrm{N}$ :占 24 bits ,其中 23 位显式存储
- 符号位 $\mathrm{S}$ :占 1 bit
- 指数位 $\mathrm{E}$ :占 8 bits
- 分数位 $\mathrm{N}$ :占 24 bits ,其中 23 位显式存储

设 32-bit 二进制数的第 $i$ 位为 $b_i$ ,则 `float` 值的计算方法定义为:

Expand Down Expand Up @@ -141,7 +141,7 @@ $$

特别地,次正规数显著提升了浮点数的精度,这是因为:

- 最小正正规数为 $2^{-126} \approx 1.18 \times 10^{-38}$
- 最小正次正规数为 $2^{-126} \times 2^{-23} \approx 1.4 \times 10^{-45}$
- 最小正正规数为 $2^{-126} \approx 1.18 \times 10^{-38}$
- 最小正次正规数为 $2^{-126} \times 2^{-23} \approx 1.4 \times 10^{-45}$

双精度 `double` 也采用类似 `float` 的表示方法,此处不再详述。
4 changes: 2 additions & 2 deletions docs/chapter_divide_and_conquer/binary_search_recur.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@

从原问题 $f(0, n-1)$ 为起始点,二分查找的分治步骤为:

1. 计算搜索区间 $[i, j]$ 的中点 $m$ ,根据它排除一半搜索区间
2. 递归求解规模减小一半的子问题,可能为 $f(i, m-1)$ 或 $f(m+1, j)$
1. 计算搜索区间 $[i, j]$ 的中点 $m$ ,根据它排除一半搜索区间
2. 递归求解规模减小一半的子问题,可能为 $f(i, m-1)$ 或 $f(m+1, j)$
3. 循环第 `1.` , `2.` 步,直至找到 `target` 或区间为空时返回。

下图展示了在数组中二分查找元素 $6$ 的分治过程。
Expand Down
16 changes: 8 additions & 8 deletions docs/chapter_divide_and_conquer/build_binary_tree_problem.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,24 @@

根据定义,`preorder``inorder` 都可以被划分为三个部分:

- 前序遍历:`[ 根节点 | 左子树 | 右子树 ]` ,例如上图 `[ 3 | 9 | 2 1 7 ]`
- 中序遍历:`[ 左子树 | 根节点 | 右子树 ]` ,例如上图 `[ 9 | 3 | 1 2 7 ]`
- 前序遍历:`[ 根节点 | 左子树 | 右子树 ]` ,例如上图 `[ 3 | 9 | 2 1 7 ]`
- 中序遍历:`[ 左子树 | 根节点 | 右子树 ]` ,例如上图 `[ 9 | 3 | 1 2 7 ]`

以上图数据为例,我们可以通过以下步骤得到上述的划分结果:

1. 前序遍历的首元素 3 是根节点的值
2. 查找根节点 3 在 `inorder` 中的索引,利用该索引可将 `inorder` 划分为 `[ 9 | 3 | 1 2 7 ]`
3. 根据 `inorder` 划分结果,易得左子树和右子树的节点数量分别为 1 和 3 ,从而可将 `preorder` 划分为 `[ 3 | 9 | 2 1 7 ]`
1. 前序遍历的首元素 3 是根节点的值
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)

### 基于变量描述子树区间

根据以上划分方法,**我们已经得到根节点、左子树、右子树在 `preorder``inorder` 中的索引区间**。而为了描述这些索引区间,我们需要借助几个指针变量:

- 将当前树的根节点在 `preorder` 中的索引记为 $i$
- 将当前树的根节点在 `inorder` 中的索引记为 $m$
- 将当前树在 `inorder` 中的索引区间记为 $[l, r]$
- 将当前树的根节点在 `preorder` 中的索引记为 $i$
- 将当前树的根节点在 `inorder` 中的索引记为 $m$
- 将当前树在 `inorder` 中的索引区间记为 $[l, r]$

如下表所示,通过以上变量即可表示根节点在 `preorder` 中的索引,以及子树在 `inorder` 中的索引区间。

Expand Down
10 changes: 5 additions & 5 deletions docs/chapter_divide_and_conquer/divide_and_conquer.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

「分治 Divide and Conquer」,全称分而治之,是一种非常重要且常见的算法策略。分治通常基于递归实现,包括“分”和“治”两步:

1. **分(划分阶段)**:递归地将原问题分解为两个或多个子问题,直至到达最小子问题时终止
2. **治(合并阶段)**:从已知解的最小子问题开始,从底至顶地将子问题的解进行合并,从而构建出原问题的解
1. **分(划分阶段)**:递归地将原问题分解为两个或多个子问题,直至到达最小子问题时终止
2. **治(合并阶段)**:从已知解的最小子问题开始,从底至顶地将子问题的解进行合并,从而构建出原问题的解

已介绍过的「归并排序」是分治策略的典型应用之一,它的分治策略为:

Expand All @@ -22,9 +22,9 @@

显然归并排序,满足以上三条判断依据:

1. 递归地将数组(原问题)划分为两个子数组(子问题)
2. 每个子数组都可以独立地进行排序(子问题可以独立进行求解)
3. 两个有序子数组(子问题的解)可以被合并为一个有序数组(原问题的解)
1. 递归地将数组(原问题)划分为两个子数组(子问题)
2. 每个子数组都可以独立地进行排序(子问题可以独立进行求解)
3. 两个有序子数组(子问题的解)可以被合并为一个有序数组(原问题的解)

## 通过分治提升效率

Expand Down
Loading

0 comments on commit 63a0e73

Please sign in to comment.