mirror of
https://github.com/krahets/hello-algo.git
synced 2025-12-16 03:59:18 +08:00
Mention figures and tables in normal texts.
Fix some figures. Finetune texts.
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 72 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 82 KiB |
@@ -671,7 +671,7 @@ $$
|
||||
|
||||
常数阶常见于数量与输入数据大小 $n$ 无关的常量、变量、对象。
|
||||
|
||||
需要注意的是,在循环中初始化变量或调用函数而占用的内存,在进入下一循环后就会被释放,即不会累积占用空间,空间复杂度仍为 $O(1)$ :
|
||||
需要注意的是,在循环中初始化变量或调用函数而占用的内存,在进入下一循环后就会被释放,因此不会累积占用空间,空间复杂度仍为 $O(1)$ :
|
||||
|
||||
=== "Java"
|
||||
|
||||
@@ -847,7 +847,7 @@ $$
|
||||
[class]{}-[func]{linear}
|
||||
```
|
||||
|
||||
以下函数的递归深度为 $n$ ,即同时存在 $n$ 个未返回的 `linear_recur()` 函数,使用 $O(n)$ 大小的栈帧空间:
|
||||
如下图所示,此函数的递归深度为 $n$ ,即同时存在 $n$ 个未返回的 `linear_recur()` 函数,使用 $O(n)$ 大小的栈帧空间:
|
||||
|
||||
=== "Java"
|
||||
|
||||
@@ -999,7 +999,7 @@ $$
|
||||
[class]{}-[func]{quadratic}
|
||||
```
|
||||
|
||||
以下函数的递归深度为 $n$ ,在每个递归函数中都初始化了一个数组,长度分别为 $n, n-1, n-2, ..., 2, 1$ ,平均长度为 $n / 2$ ,因此总体占用 $O(n^2)$ 空间。
|
||||
如下图所示,该函数的递归深度为 $n$ ,在每个递归函数中都初始化了一个数组,长度分别为 $n, n-1, n-2, ..., 2, 1$ ,平均长度为 $n / 2$ ,因此总体占用 $O(n^2)$ 空间:
|
||||
|
||||
=== "Java"
|
||||
|
||||
@@ -1077,7 +1077,7 @@ $$
|
||||
|
||||
### 指数阶 $O(2^n)$
|
||||
|
||||
指数阶常见于二叉树。高度为 $n$ 的“满二叉树”的节点数量为 $2^n - 1$ ,占用 $O(2^n)$ 空间:
|
||||
指数阶常见于二叉树。观察下图,高度为 $n$ 的“满二叉树”的节点数量为 $2^n - 1$ ,占用 $O(2^n)$ 空间:
|
||||
|
||||
=== "Java"
|
||||
|
||||
|
||||
@@ -426,11 +426,11 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
算法 `A` 只有 $1$ 个打印操作,算法运行时间不随着 $n$ 增大而增长。我们称此算法的时间复杂度为“常数阶”。
|
||||
下图展示了以上三个算法函数的时间复杂度。
|
||||
|
||||
算法 `B` 中的打印操作需要循环 $n$ 次,算法运行时间随着 $n$ 增大呈线性增长。此算法的时间复杂度被称为“线性阶”。
|
||||
|
||||
算法 `C` 中的打印操作需要循环 $1000000$ 次,虽然运行时间很长,但它与输入数据大小 $n$ 无关。因此 `C` 的时间复杂度和 `A` 相同,仍为“常数阶”。
|
||||
- 算法 `A` 只有 $1$ 个打印操作,算法运行时间不随着 $n$ 增大而增长。我们称此算法的时间复杂度为“常数阶”。
|
||||
- 算法 `B` 中的打印操作需要循环 $n$ 次,算法运行时间随着 $n$ 增大呈线性增长。此算法的时间复杂度被称为“线性阶”。
|
||||
- 算法 `C` 中的打印操作需要循环 $1000000$ 次,虽然运行时间很长,但它与输入数据大小 $n$ 无关。因此 `C` 的时间复杂度和 `A` 相同,仍为“常数阶”。
|
||||
|
||||

|
||||
|
||||
@@ -871,7 +871,7 @@ $$
|
||||
|
||||
**时间复杂度由多项式 $T(n)$ 中最高阶的项来决定**。这是因为在 $n$ 趋于无穷大时,最高阶的项将发挥主导作用,其他项的影响都可以被忽略。
|
||||
|
||||
以下表格展示了一些例子,其中一些夸张的值是为了强调“系数无法撼动阶数”这一结论。当 $n$ 趋于无穷大时,这些常数变得无足轻重。
|
||||
下表展示了一些例子,其中一些夸张的值是为了强调“系数无法撼动阶数”这一结论。当 $n$ 趋于无穷大时,这些常数变得无足轻重。
|
||||
|
||||
<p align="center"> 表:不同操作数量对应的时间复杂度 </p>
|
||||
|
||||
@@ -885,7 +885,7 @@ $$
|
||||
|
||||
## 常见类型
|
||||
|
||||
设输入数据大小为 $n$ ,常见的时间复杂度类型包括(按照从低到高的顺序排列):
|
||||
设输入数据大小为 $n$ ,常见的时间复杂度类型如下图所示(按照从低到高的顺序排列)。
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
@@ -1286,7 +1286,9 @@ $$
|
||||
|
||||
### 指数阶 $O(2^n)$
|
||||
|
||||
生物学的“细胞分裂”是指数阶增长的典型例子:初始状态为 $1$ 个细胞,分裂一轮后变为 $2$ 个,分裂两轮后变为 $4$ 个,以此类推,分裂 $n$ 轮后有 $2^n$ 个细胞。相关代码如下:
|
||||
生物学的“细胞分裂”是指数阶增长的典型例子:初始状态为 $1$ 个细胞,分裂一轮后变为 $2$ 个,分裂两轮后变为 $4$ 个,以此类推,分裂 $n$ 轮后有 $2^n$ 个细胞。
|
||||
|
||||
以下代码和图模拟了细胞分裂的过程,时间复杂度为 $O(2^n)$ 。
|
||||
|
||||
=== "Java"
|
||||
|
||||
@@ -1360,11 +1362,9 @@ $$
|
||||
[class]{}-[func]{exponential}
|
||||
```
|
||||
|
||||
下图展示了细胞分裂的过程。
|
||||
|
||||

|
||||
|
||||
在实际算法中,指数阶常出现于递归函数中。例如以下代码,其递归地一分为二,经过 $n$ 次分裂后停止:
|
||||
在实际算法中,指数阶常出现于递归函数中。例如在以下代码中,其递归地一分为二,经过 $n$ 次分裂后停止:
|
||||
|
||||
=== "Java"
|
||||
|
||||
@@ -1442,7 +1442,9 @@ $$
|
||||
|
||||
### 对数阶 $O(\log n)$
|
||||
|
||||
与指数阶相反,对数阶反映了“每轮缩减到一半”的情况。设输入数据大小为 $n$ ,由于每轮缩减到一半,因此循环次数是 $\log_2 n$ ,即 $2^n$ 的反函数。相关代码如下:
|
||||
与指数阶相反,对数阶反映了“每轮缩减到一半”的情况。设输入数据大小为 $n$ ,由于每轮缩减到一半,因此循环次数是 $\log_2 n$ ,即 $2^n$ 的反函数。
|
||||
|
||||
以下代码和图模拟了“每轮缩减到一半”的过程,时间复杂度为 $O(\log_2 n)$ ,简记为 $O(\log n)$ 。
|
||||
|
||||
=== "Java"
|
||||
|
||||
@@ -1602,7 +1604,7 @@ $$
|
||||
O(\log_m n) = O(\log_k n / \log_k m) = O(\log_k n)
|
||||
$$
|
||||
|
||||
因此我们通常会省略底数 $m$ ,将对数阶直接记为 $O(\log n)$ 。
|
||||
也就是说,底数 $m$ 可以在不影响复杂度的前提下转换。因此我们通常会省略底数 $m$ ,将对数阶直接记为 $O(\log n)$ 。
|
||||
|
||||
### 线性对数阶 $O(n \log n)$
|
||||
|
||||
@@ -1680,6 +1682,8 @@ $$
|
||||
[class]{}-[func]{linear_log_recur}
|
||||
```
|
||||
|
||||
下图展示了线性对数阶的生成方式。二叉树的每一层的操作总数都为 $n$ ,树共有 $\log_2 n + 1$ 层,因此时间复杂度为 $O(n \log n)$ 。
|
||||
|
||||

|
||||
|
||||
主流排序算法的时间复杂度通常为 $O(n \log n)$ ,例如快速排序、归并排序、堆排序等。
|
||||
@@ -1692,7 +1696,7 @@ $$
|
||||
n! = n \times (n - 1) \times (n - 2) \times \dots \times 2 \times 1
|
||||
$$
|
||||
|
||||
阶乘通常使用递归实现。例如在以下代码中,第一层分裂出 $n$ 个,第二层分裂出 $n - 1$ 个,以此类推,直至第 $n$ 层时停止分裂:
|
||||
阶乘通常使用递归实现。如下图和以下代码所示,第一层分裂出 $n$ 个,第二层分裂出 $n - 1$ 个,以此类推,直至第 $n$ 层时停止分裂:
|
||||
|
||||
=== "Java"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user