Add figure and table numbers in normal texts.

pull/691/head
krahets 1 year ago
parent 106f02809a
commit 1aa558bd2d

@ -713,7 +713,7 @@
下表总结对比了数组和链表的各项特点与操作效率。由于它们采用两种相反的存储策略,因此各种性质和操作效率也呈现对立的特点。 下表总结对比了数组和链表的各项特点与操作效率。由于它们采用两种相反的存储策略,因此各种性质和操作效率也呈现对立的特点。
<p align="center">数组与链表的效率对比 </p> <p align="center"> <id> &nbsp; 数组与链表的效率对比 </p>
| | 数组 | 链表 | | | 数组 | 链表 |
| ---------- | ------------------------ | ------------ | | ---------- | ------------------------ | ------------ |

@ -4,7 +4,7 @@
下表列举了几个示例数据,包括输入数组和对应的所有排列。 下表列举了几个示例数据,包括输入数组和对应的所有排列。
<p align="center">数组与链表的效率对比 </p> <p align="center"> <id> &nbsp; 数组与链表的效率对比 </p>
| 输入数组 | 所有排列 | | 输入数组 | 所有排列 |
| :---------- | :----------------------------------------------------------------- | | :---------- | :----------------------------------------------------------------- |

@ -873,7 +873,7 @@ $$
下表展示了一些例子,其中一些夸张的值是为了强调“系数无法撼动阶数”这一结论。当 $n$ 趋于无穷大时,这些常数变得无足轻重。 下表展示了一些例子,其中一些夸张的值是为了强调“系数无法撼动阶数”这一结论。当 $n$ 趋于无穷大时,这些常数变得无足轻重。
<p align="center">不同操作数量对应的时间复杂度 </p> <p align="center"> <id> &nbsp; 不同操作数量对应的时间复杂度 </p>
| 操作数量 $T(n)$ | 时间复杂度 $O(f(n))$ | | 操作数量 $T(n)$ | 时间复杂度 $O(f(n))$ |
| ---------------------- | -------------------- | | ---------------------- | -------------------- |
@ -1288,7 +1288,7 @@ $$
生物学的“细胞分裂”是指数阶增长的典型例子:初始状态为 $1$ 个细胞,分裂一轮后变为 $2$ 个,分裂两轮后变为 $4$ 个,以此类推,分裂 $n$ 轮后有 $2^n$ 个细胞。 生物学的“细胞分裂”是指数阶增长的典型例子:初始状态为 $1$ 个细胞,分裂一轮后变为 $2$ 个,分裂两轮后变为 $4$ 个,以此类推,分裂 $n$ 轮后有 $2^n$ 个细胞。
以下代码和图模拟了细胞分裂的过程,时间复杂度为 $O(2^n)$ 。 下图和以下代码模拟了细胞分裂的过程,时间复杂度为 $O(2^n)$ 。
=== "Java" === "Java"
@ -1444,7 +1444,7 @@ $$
与指数阶相反,对数阶反映了“每轮缩减到一半”的情况。设输入数据大小为 $n$ ,由于每轮缩减到一半,因此循环次数是 $\log_2 n$ ,即 $2^n$ 的反函数。 与指数阶相反,对数阶反映了“每轮缩减到一半”的情况。设输入数据大小为 $n$ ,由于每轮缩减到一半,因此循环次数是 $\log_2 n$ ,即 $2^n$ 的反函数。
以下代码和图模拟了“每轮缩减到一半”的过程,时间复杂度为 $O(\log_2 n)$ ,简记为 $O(\log n)$ 。 下图和以下代码模拟了“每轮缩减到一半”的过程,时间复杂度为 $O(\log_2 n)$ ,简记为 $O(\log n)$ 。
=== "Java" === "Java"

@ -18,7 +18,7 @@
下表列举了各种基本数据类型的占用空间、取值范围和默认值。此表格无须硬背,大致理解即可,需要时可以通过查表来回忆。 下表列举了各种基本数据类型的占用空间、取值范围和默认值。此表格无须硬背,大致理解即可,需要时可以通过查表来回忆。
<p align="center">基本数据类型的占用空间和取值范围 </p> <p align="center"> <id> &nbsp; 基本数据类型的占用空间和取值范围 </p>
| 类型 | 符号 | 占用空间 | 最小值 | 最大值 | 默认值 | | 类型 | 符号 | 占用空间 | 最小值 | 最大值 | 默认值 |
| ------ | -------- | ---------------- | ------------------------ | ----------------------- | -------------- | | ------ | -------- | ---------------- | ------------------------ | ----------------------- | -------------- |

@ -137,7 +137,7 @@ $$
如下表所示,指数位 $E = 0$ 和 $E = 255$ 具有特殊含义,**用于表示零、无穷大、$\mathrm{NaN}$ 等**。 如下表所示,指数位 $E = 0$ 和 $E = 255$ 具有特殊含义,**用于表示零、无穷大、$\mathrm{NaN}$ 等**。
<p align="center">指数位含义 </p> <p align="center"> <id> &nbsp; 指数位含义 </p>
| 指数位 E | 分数位 $\mathrm{N} = 0$ | 分数位 $\mathrm{N} \ne 0$ | 计算公式 | | 指数位 E | 分数位 $\mathrm{N} = 0$ | 分数位 $\mathrm{N} \ne 0$ | 计算公式 |
| ------------------ | ----------------------- | ------------------------- | ---------------------------------------------------------------------- | | ------------------ | ----------------------- | ------------------------- | ---------------------------------------------------------------------- |

@ -20,8 +20,8 @@
根据定义,`preorder` 和 `inorder` 都可以被划分为三个部分: 根据定义,`preorder` 和 `inorder` 都可以被划分为三个部分:
- 前序遍历:`[ 根节点 | 左子树 | 右子树 ]` ,例如上图 `[ 3 | 9 | 2 1 7 ]` - 前序遍历:`[ 根节点 | 左子树 | 右子树 ]` ,例如上图的树对应 `[ 3 | 9 | 2 1 7 ]`
- 中序遍历:`[ 左子树 | 根节点 右子树 ]` ,例如上图 `[ 9 | 3 | 1 2 7 ]` - 中序遍历:`[ 左子树 | 根节点 右子树 ]` ,例如上图的树对应 `[ 9 | 3 | 1 2 7 ]`
以上图数据为例,我们可以通过下图所示的步骤得到划分结果: 以上图数据为例,我们可以通过下图所示的步骤得到划分结果:
@ -41,7 +41,7 @@
如下表所示,通过以上变量即可表示根节点在 `preorder` 中的索引,以及子树在 `inorder` 中的索引区间。 如下表所示,通过以上变量即可表示根节点在 `preorder` 中的索引,以及子树在 `inorder` 中的索引区间。
<p align="center">根节点和子树在前序和中序遍历中的索引 </p> <p align="center"> <id> &nbsp; 根节点和子树在前序和中序遍历中的索引 </p>
| | 根节点在 `preorder` 中的索引 | 子树在 `inorder` 中的索引区间 | | | 根节点在 `preorder` 中的索引 | 子树在 `inorder` 中的索引区间 |
| ------ | -------------------------------- | ----------------------------- | | ------ | -------------------------------- | ----------------------------- |

@ -54,7 +54,6 @@
2. 将 `A` 中剩余的一个圆盘从 `A` 直接移动至 `C` 2. 将 `A` 中剩余的一个圆盘从 `A` 直接移动至 `C`
3. 令 `C` 为目标柱、`A` 为缓冲柱,将两个圆盘从 `B` 移动至 `C` 3. 令 `C` 为目标柱、`A` 为缓冲柱,将两个圆盘从 `B` 移动至 `C`
=== "<1>" === "<1>"
![规模为 3 问题的解](hanota_problem.assets/hanota_f3_step1.png) ![规模为 3 问题的解](hanota_problem.assets/hanota_f3_step1.png)

@ -239,7 +239,7 @@ $$
![爬楼梯对应递归树](intro_to_dynamic_programming.assets/climbing_stairs_dfs_tree.png) ![爬楼梯对应递归树](intro_to_dynamic_programming.assets/climbing_stairs_dfs_tree.png)
观察上图发现**指数阶的时间复杂度是由于“重叠子问题”导致的**。例如$dp[9]$ 被分解为 $dp[8]$ 和 $dp[7]$ $dp[8]$ 被分解为 $dp[7]$ 和 $dp[6]$ ,两者都包含子问题 $dp[7]$ 。 观察上图,**指数阶的时间复杂度是由于“重叠子问题”导致的**。例如 $dp[9]$ 被分解为 $dp[8]$ 和 $dp[7]$ $dp[8]$ 被分解为 $dp[7]$ 和 $dp[6]$ ,两者都包含子问题 $dp[7]$ 。
以此类推,子问题中包含更小的重叠子问题,子子孙孙无穷尽也。绝大部分计算资源都浪费在这些重叠的问题上。 以此类推,子问题中包含更小的重叠子问题,子子孙孙无穷尽也。绝大部分计算资源都浪费在这些重叠的问题上。

@ -72,9 +72,9 @@ $$
## 图常见应用 ## 图常见应用
如下所示,许多现实系统都可以用图来建模,相应的问题也可以约化为图计算问题。 如下所示,许多现实系统都可以用图来建模,相应的问题也可以约化为图计算问题。
<p align="center">现实生活中常见的图 </p> <p align="center"> <id> &nbsp; 现实生活中常见的图 </p>
| | 顶点 | 边 | 图计算问题 | | | 顶点 | 边 | 图计算问题 |
| ------ | ---- | --------------- | ------------ | | ------ | ---- | --------------- | ------------ |

@ -207,7 +207,7 @@
设图中共有 $n$ 个顶点和 $m$ 条边,下表对比了邻接矩阵和邻接表的时间和空间效率。 设图中共有 $n$ 个顶点和 $m$ 条边,下表对比了邻接矩阵和邻接表的时间和空间效率。
<p align="center">邻接矩阵与邻接表对比 </p> <p align="center"> <id> &nbsp; 邻接矩阵与邻接表对比 </p>
| | 邻接矩阵 | 邻接表(链表) | 邻接表(哈希表) | | | 邻接矩阵 | 邻接表(链表) | 邻接表(哈希表) |
| ------------ | -------- | -------------- | ---------------- | | ------------ | -------- | -------------- | ---------------- |

@ -2,7 +2,7 @@
「哈希表 hash table」又称「散列表」其通过建立键 `key` 与值 `value` 之间的映射,实现高效的元素查询。具体而言,我们向哈希表输入一个键 `key` ,则可以在 $O(1)$ 时间内获取对应的值 `value` 「哈希表 hash table」又称「散列表」其通过建立键 `key` 与值 `value` 之间的映射,实现高效的元素查询。具体而言,我们向哈希表输入一个键 `key` ,则可以在 $O(1)$ 时间内获取对应的值 `value`
如下图所示,给定 $n$ 个学生,每个学生都有“姓名”和“学号”两项数据。假如我们希望实现“输入一个学号,返回对应的姓名”的查询功能,则可以采用哈希表来实现。 如下图所示,给定 $n$ 个学生,每个学生都有“姓名”和“学号”两项数据。假如我们希望实现“输入一个学号,返回对应的姓名”的查询功能,则可以采用下图所示的哈希表来实现。
![哈希表的抽象表示](hash_map.assets/hash_table_lookup.png) ![哈希表的抽象表示](hash_map.assets/hash_table_lookup.png)
@ -12,7 +12,7 @@
- **查询元素**:由于数组(链表)是乱序的,因此需要遍历其中的所有元素,使用 $O(n)$ 时间。 - **查询元素**:由于数组(链表)是乱序的,因此需要遍历其中的所有元素,使用 $O(n)$ 时间。
- **删除元素**:需要先查询到元素,再从数组(链表)中删除,使用 $O(n)$ 时间。 - **删除元素**:需要先查询到元素,再从数组(链表)中删除,使用 $O(n)$ 时间。
<p align="center">元素查询效率对比 </p> <p align="center"> <id> &nbsp; 元素查询效率对比 </p>
| | 数组 | 链表 | 哈希表 | | | 数组 | 链表 | 哈希表 |
| -------- | ------ | ------ | ------ | | -------- | ------ | ------ | ------ |

@ -21,7 +21,7 @@
堆的常用操作见下表,方法名需要根据编程语言来确定。 堆的常用操作见下表,方法名需要根据编程语言来确定。
<p align="center">堆的操作效率 </p> <p align="center"> <id> &nbsp; 堆的操作效率 </p>
| 方法名 | 描述 | 时间复杂度 | | 方法名 | 描述 | 时间复杂度 |
| --------- | ------------------------------------------ | ----------- | | --------- | ------------------------------------------ | ----------- |
@ -58,11 +58,11 @@
/* 堆顶元素出堆 */ /* 堆顶元素出堆 */
// 出堆元素会形成一个从大到小的序列 // 出堆元素会形成一个从大到小的序列
peek = heap.poll(); // 5 peek = maxHeap.poll(); // 5
peek = heap.poll(); // 4 peek = maxHeap.poll(); // 4
peek = heap.poll(); // 3 peek = maxHeap.poll(); // 3
peek = heap.poll(); // 2 peek = maxHeap.poll(); // 2
peek = heap.poll(); // 1 peek = maxHeap.poll(); // 1
/* 获取堆大小 */ /* 获取堆大小 */
int size = maxHeap.size(); int size = maxHeap.size();

@ -37,7 +37,7 @@
两者的详细对应关系如下表所示。 两者的详细对应关系如下表所示。
<p align="center">将数据结构与算法类比为积木 </p> <p align="center"> <id> &nbsp; 将数据结构与算法类比为积木 </p>
| 数据结构与算法 | 拼装积木 | | 数据结构与算法 | 拼装积木 |
| -------------- | ---------------------------------------- | | -------------- | ---------------------------------------- |

@ -188,11 +188,11 @@
git clone https://github.com/krahets/hello-algo.git git clone https://github.com/krahets/hello-algo.git
``` ```
当然你也可以点击“Download ZIP”直接下载代码压缩包然后在本地解压即可。 当然,你也可以在下图所示的位置,点击“Download ZIP”直接下载代码压缩包然后在本地解压即可。
![克隆仓库与下载代码](suggestions.assets/download_code.png) ![克隆仓库与下载代码](suggestions.assets/download_code.png)
**第三步:运行源代码**。如果代码块顶部标有文件名称,则可以在仓库的 `codes` 文件夹中找到相应的源代码文件。源代码文件将帮助你节省不必要的调试时间,让你能够专注于学习内容。 **第三步:运行源代码**。如下图所示,对于顶部标有文件名称的代码块,我们可以在仓库的 `codes` 文件夹内找到对应的源代码文件。源代码文件可一键运行,将帮助你节省不必要的调试时间,让你能够专注于学习内容。
![代码块与对应的源代码文件](suggestions.assets/code_md_to_repo.png) ![代码块与对应的源代码文件](suggestions.assets/code_md_to_repo.png)
@ -200,7 +200,7 @@ git clone https://github.com/krahets/hello-algo.git
在阅读本书时,请不要轻易跳过那些没学明白的知识点。**欢迎在评论区提出你的问题**,我和小伙伴们将竭诚为你解答,一般情况下可在两天内回复。 在阅读本书时,请不要轻易跳过那些没学明白的知识点。**欢迎在评论区提出你的问题**,我和小伙伴们将竭诚为你解答,一般情况下可在两天内回复。
同时,也希望你能在评论区多花些时间。一方面,你可以了解大家遇到的问题,从而查漏补缺,激发更深入的思考。另一方面,期待你能慷慨地回答其他小伙伴的问题,分享您的见解,帮助他人进步。 如下图所示,每篇文章的底部都配有评论区。希望你能多关注评论区的内容。一方面,你可以了解大家遇到的问题,从而查漏补缺,激发更深入的思考。另一方面,期待你能慷慨地回答其他小伙伴的问题,分享您的见解,帮助他人进步。
![评论区示例](../index.assets/comment.gif) ![评论区示例](../index.assets/comment.gif)

@ -44,7 +44,7 @@
上述几种方法的操作效率与特性如下表所示。 上述几种方法的操作效率与特性如下表所示。
<p align="center">查找算法效率对比 </p> <p align="center"> <id> &nbsp; 查找算法效率对比 </p>
| | 线性搜索 | 二分查找 | 树查找 | 哈希查找 | | | 线性搜索 | 二分查找 | 树查找 | 哈希查找 |
| ------------ | -------- | ------------------ | ------------------ | --------------- | | ------------ | -------- | ------------------ | ------------------ | --------------- |
@ -55,7 +55,7 @@
| 数据预处理 | / | 排序 $O(n \log n)$ | 建树 $O(n \log n)$ | 建哈希表 $O(n)$ | | 数据预处理 | / | 排序 $O(n \log n)$ | 建树 $O(n \log n)$ | 建哈希表 $O(n)$ |
| 数据是否有序 | 无序 | 有序 | 有序 | 无序 | | 数据是否有序 | 无序 | 有序 | 有序 | 无序 |
除了以上表格内容,搜索算法的选择还取决于数据体量、搜索性能要求、数据查询与更新频率等。 搜索算法的选择还取决于数据体量、搜索性能要求、数据查询与更新频率等。
**线性搜索** **线性搜索**

@ -8,7 +8,7 @@
双向队列的常用操作如下表所示,具体的方法名称需要根据所使用的编程语言来确定。 双向队列的常用操作如下表所示,具体的方法名称需要根据所使用的编程语言来确定。
<p align="center">双向队列操作效率 </p> <p align="center"> <id> &nbsp; 双向队列操作效率 </p>
| 方法名 | 描述 | 时间复杂度 | | 方法名 | 描述 | 时间复杂度 |
| ----------- | -------------- | ---------- | | ----------- | -------------- | ---------- |

@ -10,7 +10,7 @@
队列的常见操作如下表所示。需要注意的是,不同编程语言的方法名称可能会有所不同。我们在此采用与栈相同的方法命名。 队列的常见操作如下表所示。需要注意的是,不同编程语言的方法名称可能会有所不同。我们在此采用与栈相同的方法命名。
<p align="center">队列操作效率 </p> <p align="center"> <id> &nbsp; 队列操作效率 </p>
| 方法名 | 描述 | 时间复杂度 | | 方法名 | 描述 | 时间复杂度 |
| --------- | -------------------------- | -------- | | --------- | -------------------------- | -------- |

@ -12,7 +12,7 @@
栈的常用操作如下表所示,具体的方法名需要根据所使用的编程语言来确定。在此,我们以常见的 `push()` , `pop()` , `peek()` 命名为例。 栈的常用操作如下表所示,具体的方法名需要根据所使用的编程语言来确定。在此,我们以常见的 `push()` , `pop()` , `peek()` 命名为例。
<p align="center">栈的操作效率 </p> <p align="center"> <id> &nbsp; 栈的操作效率 </p>
| 方法 | 描述 | 时间复杂度 | | 方法 | 描述 | 时间复杂度 |
| --------- | ---------------------- | ---------- | | --------- | ---------------------- | ---------- |

@ -570,7 +570,7 @@ AVL 树的特点在于“旋转”操作,它能够在不影响二叉树的中
如下表所示,我们通过判断失衡节点的平衡因子以及较高一侧子节点的平衡因子的正负号,来确定失衡节点属于上图中的哪种情况。 如下表所示,我们通过判断失衡节点的平衡因子以及较高一侧子节点的平衡因子的正负号,来确定失衡节点属于上图中的哪种情况。
<p align="center">四种旋转情况的选择条件 </p> <p align="center"> <id> &nbsp; 四种旋转情况的选择条件 </p>
| 失衡节点的平衡因子 | 子节点的平衡因子 | 应采用的旋转方法 | | 失衡节点的平衡因子 | 子节点的平衡因子 | 应采用的旋转方法 |
| ---------------- | ---------------- | ---------------- | | ---------------- | ---------------- | ---------------- |

@ -314,7 +314,7 @@
观察下表,二叉搜索树的各项操作的时间复杂度都是对数阶,具有稳定且高效的性能表现。只有在高频添加、低频查找删除的数据适用场景下,数组比二叉搜索树的效率更高。 观察下表,二叉搜索树的各项操作的时间复杂度都是对数阶,具有稳定且高效的性能表现。只有在高频添加、低频查找删除的数据适用场景下,数组比二叉搜索树的效率更高。
<p align="center">数组与搜索树的效率对比 </p> <p align="center"> <id> &nbsp; 数组与搜索树的效率对比 </p>
| | 无序数组 | 二叉搜索树 | | | 无序数组 | 二叉搜索树 |
| -------- | -------- | ----------- | | -------- | -------- | ----------- |

@ -372,7 +372,7 @@
``` ```
## 插入与删除节点 ### 插入与删除节点
与链表类似,在二叉树中插入与删除节点可以通过修改指针来实现。下图给出了一个示例。 与链表类似,在二叉树中插入与删除节点可以通过修改指针来实现。下图给出了一个示例。
@ -553,7 +553,7 @@
如下表所示,在最佳和最差结构下,二叉树的叶节点数量、节点总数、高度等达到极大或极小值。 如下表所示,在最佳和最差结构下,二叉树的叶节点数量、节点总数、高度等达到极大或极小值。
<p align="center">二叉树的最佳与最差情况 </p> <p align="center"> <id> &nbsp; 二叉树的最佳与最差情况 </p>
| | 完美二叉树 | 链表 | | | 完美二叉树 | 链表 |
| ----------------------------- | ---------- | ---------- | | ----------------------------- | ---------- | ---------- |

Loading…
Cancel
Save