Update the chapter of

introduction and complexity anylysis.
pull/697/head
krahets 1 year ago
parent 50e17905fd
commit 8347c2da36

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

@ -1,8 +1,8 @@
# 时空复杂度 # 复杂度分析
<div class="center-table" markdown> <div class="center-table" markdown>
![时空复杂度](../assets/covers/chapter_complexity_analysis.jpg){ width="600" } ![复杂度分析](../assets/covers/chapter_complexity_analysis.jpg){ width="600" }
</div> </div>

@ -5,7 +5,7 @@
1. **找到问题解法**:算法需要在规定的输入范围内,可靠地求得问题的正确解。 1. **找到问题解法**:算法需要在规定的输入范围内,可靠地求得问题的正确解。
2. **寻求最优解法**:同一个问题可能存在多种解法,我们希望找到尽可能高效的算法。 2. **寻求最优解法**:同一个问题可能存在多种解法,我们希望找到尽可能高效的算法。
也就是说,在能够解决问题的前提下,算法效率衡量算法优劣的主要评价指标,它包括以下两个维度。 也就是说,在能够解决问题的前提下,算法效率已成为衡量算法优劣的主要评价指标,它包括以下两个维度。
- **时间效率**:算法运行速度的快慢。 - **时间效率**:算法运行速度的快慢。
- **空间效率**:算法占用内存空间的大小。 - **空间效率**:算法占用内存空间的大小。
@ -20,7 +20,7 @@
一方面,**难以排除测试环境的干扰因素**。硬件配置会影响算法的性能表现。比如在某台计算机中,算法 `A` 的运行时间比算法 `B` 短;但在另一台配置不同的计算机中,我们可能得到相反的测试结果。这意味着我们需要在各种机器上进行测试,统计平均效率,而这是不现实的。 一方面,**难以排除测试环境的干扰因素**。硬件配置会影响算法的性能表现。比如在某台计算机中,算法 `A` 的运行时间比算法 `B` 短;但在另一台配置不同的计算机中,我们可能得到相反的测试结果。这意味着我们需要在各种机器上进行测试,统计平均效率,而这是不现实的。
另一方面,**展开完整测试非常耗费资源**。随着输入数据量的变化,算法会表现出不同的效率。例如,在输入数据量较小时,算法 `A` 的运行时间比算法 `B` 更少;而输入数据量较大时,测试结果可能恰恰相反。因此,为了得到有说服力的结论,我们需要测试各种规模的输入数据,而这需要耗费大量的计算资源。 另一方面,**展开完整测试非常耗费资源**。随着输入数据量的变化,算法会表现出不同的效率。例如,在输入数据量较小时,算法 `A` 的运行时间比算法 `B` 更少;而输入数据量较大时,测试结果可能恰恰相反。因此,为了得到有说服力的结论,我们需要测试各种规模的输入数据,而这需要耗费大量的计算资源。
## 理论估算 ## 理论估算

@ -663,10 +663,6 @@ $$
![常见的空间复杂度类型](space_complexity.assets/space_complexity_common_types.png) ![常见的空间复杂度类型](space_complexity.assets/space_complexity_common_types.png)
!!! tip
部分示例代码需要一些前置知识,包括数组、链表、二叉树、递归算法等。如果你遇到看不懂的地方,可以在学完后面章节后再来复习。
### 常数阶 $O(1)$ ### 常数阶 $O(1)$
常数阶常见于数量与输入数据大小 $n$ 无关的常量、变量、对象。 常数阶常见于数量与输入数据大小 $n$ 无关的常量、变量、对象。

@ -11,7 +11,7 @@
- 时间复杂度用于衡量算法运行时间随数据量增长的趋势,可以有效评估算法效率,但在某些情况下可能失效,如在输入的数据量较小或时间复杂度相同时,无法精确对比算法效率的优劣。 - 时间复杂度用于衡量算法运行时间随数据量增长的趋势,可以有效评估算法效率,但在某些情况下可能失效,如在输入的数据量较小或时间复杂度相同时,无法精确对比算法效率的优劣。
- 最差时间复杂度使用大 $O$ 符号表示,对应函数渐近上界,反映当 $n$ 趋向正无穷时,操作数量 $T(n)$ 的增长级别。 - 最差时间复杂度使用大 $O$ 符号表示,对应函数渐近上界,反映当 $n$ 趋向正无穷时,操作数量 $T(n)$ 的增长级别。
- 推算时间复杂度分为两步,首先统计操作数量,然后判断渐近上界。 - 推算时间复杂度分为两步,首先统计操作数量,然后判断渐近上界。
- 常见时间复杂度从小到大排列有 $O(1)$、$O(\log n)$、$O(n)$、$O(n \log n)$、$O(n^2)$、$O(2^n)$$O(n!)$ 等。 - 常见时间复杂度从小到大排列有 $O(1)$、$O(\log n)$、$O(n)$、$O(n \log n)$、$O(n^2)$、$O(2^n)$$O(n!)$ 等。
- 某些算法的时间复杂度非固定,而是与输入数据的分布有关。时间复杂度分为最差、最佳、平均时间复杂度,最佳时间复杂度几乎不用,因为输入数据一般需要满足严格条件才能达到最佳情况。 - 某些算法的时间复杂度非固定,而是与输入数据的分布有关。时间复杂度分为最差、最佳、平均时间复杂度,最佳时间复杂度几乎不用,因为输入数据一般需要满足严格条件才能达到最佳情况。
- 平均时间复杂度反映算法在随机数据输入下的运行效率,最接近实际应用中的算法性能。计算平均时间复杂度需要统计输入数据分布以及综合后的数学期望。 - 平均时间复杂度反映算法在随机数据输入下的运行效率,最接近实际应用中的算法性能。计算平均时间复杂度需要统计输入数据分布以及综合后的数学期望。
@ -20,7 +20,7 @@
- 空间复杂度的作用类似于时间复杂度,用于衡量算法占用空间随数据量增长的趋势。 - 空间复杂度的作用类似于时间复杂度,用于衡量算法占用空间随数据量增长的趋势。
- 算法运行过程中的相关内存空间可分为输入空间、暂存空间、输出空间。通常情况下,输入空间不计入空间复杂度计算。暂存空间可分为指令空间、数据空间、栈帧空间,其中栈帧空间通常仅在递归函数中影响空间复杂度。 - 算法运行过程中的相关内存空间可分为输入空间、暂存空间、输出空间。通常情况下,输入空间不计入空间复杂度计算。暂存空间可分为指令空间、数据空间、栈帧空间,其中栈帧空间通常仅在递归函数中影响空间复杂度。
- 我们通常只关注最差空间复杂度,即统计算法在最差输入数据和最差运行时间点下的空间复杂度。 - 我们通常只关注最差空间复杂度,即统计算法在最差输入数据和最差运行时间点下的空间复杂度。
- 常见空间复杂度从小到大排列有 $O(1)$、$O(\log n)$、$O(n)$、$O(n^2)$$O(2^n)$ 等。 - 常见空间复杂度从小到大排列有 $O(1)$、$O(\log n)$、$O(n)$、$O(n^2)$$O(2^n)$ 等。
## Q & A ## Q & A
@ -38,7 +38,7 @@
- Java 和 C# 是面向对象的编程语言,代码块(方法)通常都是作为某个类的一部分。静态方法的行为类似于函数,因为它被绑定在类上,不能访问特定的实例变量。 - Java 和 C# 是面向对象的编程语言,代码块(方法)通常都是作为某个类的一部分。静态方法的行为类似于函数,因为它被绑定在类上,不能访问特定的实例变量。
- C++ 和 Python 既支持过程式编程(函数),也支持面向对象编程(方法)。 - C++ 和 Python 既支持过程式编程(函数),也支持面向对象编程(方法)。
!!! question "图“空间复杂度的常见类型”反映的是否是占用空间的绝对大小?" !!! question "图“常见的空间复杂度类型”反映的是否是占用空间的绝对大小?"
不是,该图片展示的是空间复杂度,其反映的是增长趋势,而不是占用空间的绝对大小。 不是,该图片展示的是空间复杂度,其反映的是增长趋势,而不是占用空间的绝对大小。

@ -613,18 +613,11 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因
我们将线性阶的时间复杂度记为 $O(n)$ ,这个数学符号称为「大 $O$ 记号 big-$O$ notation」表示函数 $T(n)$ 的「渐近上界 asymptotic upper bound」。 我们将线性阶的时间复杂度记为 $O(n)$ ,这个数学符号称为「大 $O$ 记号 big-$O$ notation」表示函数 $T(n)$ 的「渐近上界 asymptotic upper bound」。
时间复杂度分析本质上是计算“操作数量函数 $T(n)$”的渐近上界。接下来,我们来看函数渐近上界的数学定义。 时间复杂度分析本质上是计算“操作数量函数 $T(n)$”的渐近上界,其具有明确的数学定义。
!!! abstract "函数渐近上界" !!! abstract "函数渐近上界"
若存在正实数 $c$ 和实数 $n_0$ ,使得对于所有的 $n > n_0$ ,均有 若存在正实数 $c$ 和实数 $n_0$ ,使得对于所有的 $n > n_0$ ,均有 $T(n) \leq c \cdot f(n)$ ,则可认为 $f(n)$ 给出了 $T(n)$ 的一个渐近上界,记为 $T(n) = O(f(n))$ 。
$$
T(n) \leq c \cdot f(n)
$$
则可认为 $f(n)$ 给出了 $T(n)$ 的一个渐近上界,记为
$$
T(n) = O(f(n))
$$
如下图所示,计算渐近上界就是寻找一个函数 $f(n)$ ,使得当 $n$ 趋向于无穷大时,$T(n)$ 和 $f(n)$ 处于相同的增长级别,仅相差一个常数项 $c$ 的倍数。 如下图所示,计算渐近上界就是寻找一个函数 $f(n)$ ,使得当 $n$ 趋向于无穷大时,$T(n)$ 和 $f(n)$ 处于相同的增长级别,仅相差一个常数项 $c$ 的倍数。
@ -642,17 +635,9 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因
1. **忽略 $T(n)$ 中的常数项**。因为它们都与 $n$ 无关,所以对时间复杂度不产生影响。 1. **忽略 $T(n)$ 中的常数项**。因为它们都与 $n$ 无关,所以对时间复杂度不产生影响。
2. **省略所有系数**。例如,循环 $2n$ 次、$5n + 1$ 次等,都可以简化记为 $n$ 次,因为 $n$ 前面的系数对时间复杂度没有影响。 2. **省略所有系数**。例如,循环 $2n$ 次、$5n + 1$ 次等,都可以简化记为 $n$ 次,因为 $n$ 前面的系数对时间复杂度没有影响。
3. **循环嵌套时使用乘法**。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别套用上述 `1.``2.` 技巧。 3. **循环嵌套时使用乘法**。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别套用第 `1.` 点和第 `2.` 点的技巧。、
以下代码与公式分别展示了使用上述技巧前后的统计结果。两者推出的时间复杂度相同,都为 $O(n^2)$ 。
$$ 给定一个函数,我们可以用上述技巧来统计操作数量。
\begin{aligned}
T(n) & = 2n(n + 1) + (5n + 1) + 2 & \text{完整统计 (-.-|||)} \newline
& = 2n^2 + 7n + 3 \newline
T(n) & = n^2 + n & \text{偷懒统计 (o.O)}
\end{aligned}
$$
=== "Java" === "Java"
@ -867,6 +852,16 @@ $$
} }
``` ```
以下公式展示了使用上述技巧前后的统计结果,两者推出的时间复杂度都为 $O(n^2)$ 。
$$
\begin{aligned}
T(n) & = 2n(n + 1) + (5n + 1) + 2 & \text{完整统计 (-.-|||)} \newline
& = 2n^2 + 7n + 3 \newline
T(n) & = n^2 + n & \text{偷懒统计 (o.O)}
\end{aligned}
$$
### 第二步:判断渐近上界 ### 第二步:判断渐近上界
**时间复杂度由多项式 $T(n)$ 中最高阶的项来决定**。这是因为在 $n$ 趋于无穷大时,最高阶的项将发挥主导作用,其他项的影响都可以被忽略。 **时间复杂度由多项式 $T(n)$ 中最高阶的项来决定**。这是因为在 $n$ 趋于无穷大时,最高阶的项将发挥主导作用,其他项的影响都可以被忽略。
@ -896,15 +891,11 @@ $$
![常见的时间复杂度类型](time_complexity.assets/time_complexity_common_types.png) ![常见的时间复杂度类型](time_complexity.assets/time_complexity_common_types.png)
!!! tip
部分示例代码需要一些预备知识,包括数组、递归等。如果你遇到不理解的部分,可以在学完后面章节后再回顾。现阶段,请先专注于理解时间复杂度的含义和推算方法。
### 常数阶 $O(1)$ ### 常数阶 $O(1)$
常数阶的操作数量与输入数据大小 $n$ 无关,即不随着 $n$ 的变化而变化。 常数阶的操作数量与输入数据大小 $n$ 无关,即不随着 $n$ 的变化而变化。
对于以下算法,尽管操作数量 `size` 可能很大,但由于其与输入数据大小 $n$ 无关,因此时间复杂度仍为 $O(1)$ 在以下函数中,尽管操作数量 `size` 可能很大,但由于其与输入数据大小 $n$ 无关,因此时间复杂度仍为 $O(1)$
=== "Java" === "Java"
@ -1132,7 +1123,7 @@ $$
### 平方阶 $O(n^2)$ ### 平方阶 $O(n^2)$
平方阶的操作数量相对于输入数据大小以平方级别增长。平方阶通常出现在嵌套循环中,外层循环和内层循环都为 $O(n)$ ,因此总体为 $O(n^2)$ 平方阶的操作数量相对于输入数据大小 $n$ 以平方级别增长。平方阶通常出现在嵌套循环中,外层循环和内层循环都为 $O(n)$ ,因此总体为 $O(n^2)$
=== "Java" === "Java"
@ -1776,7 +1767,7 @@ $$
## 最差、最佳、平均时间复杂度 ## 最差、最佳、平均时间复杂度
**算法的时间效率往往不是固定的,而是与输入数据的分布有关**。假设输入一个长度为 $n$ 的数组 `nums` ,其中 `nums` 由从 $1$ 至 $n$ 的数字组成,每个数字只出现一次但元素顺序是随机打乱的,任务目标是返回元素 $1$ 的索引。我们可以得出以下结论。 **算法的时间效率往往不是固定的,而是与输入数据的分布有关**。假设输入一个长度为 $n$ 的数组 `nums` ,其中 `nums` 由从 $1$ 至 $n$ 的数字组成,每个数字只出现一次但元素顺序是随机打乱的,任务目标是返回元素 $1$ 的索引。我们可以得出以下结论。
- 当 `nums = [?, ?, ..., 1]` ,即当末尾元素是 $1$ 时,需要完整遍历数组,**达到最差时间复杂度 $O(n)$** 。 - 当 `nums = [?, ?, ..., 1]` ,即当末尾元素是 $1$ 时,需要完整遍历数组,**达到最差时间复杂度 $O(n)$** 。
- 当 `nums = [1, ?, ?, ...]` ,即当首个元素为 $1$ 时,无论数组多长都不需要继续遍历,**达到最佳时间复杂度 $\Omega(1)$** 。 - 当 `nums = [1, ?, ?, ...]` ,即当首个元素为 $1$ 时,无论数组多长都不需要继续遍历,**达到最佳时间复杂度 $\Omega(1)$** 。

@ -37,7 +37,7 @@
上述整理扑克牌的方法本质上是“插入排序”算法,它在处理小型数据集时非常高效。许多编程语言的排序库函数中都存在插入排序的身影。 上述整理扑克牌的方法本质上是“插入排序”算法,它在处理小型数据集时非常高效。许多编程语言的排序库函数中都存在插入排序的身影。
**例三:货币找零**。假设我们在超市购买了 $69$ 元的商品,给收银员付了 $100$ 元,则收银员需要找我们 $31$ 元。他会很自然地完成下图所示的思考。 **例三:货币找零**。假设我们在超市购买了 $69$ 元的商品,给收银员 $100$ 元,则收银员需要找我们 $31$ 元。他会很自然地完成下图所示的思考。
1. 可选项是比 $31$ 元面值更小的货币,包括 $1$ 元、$5$ 元、$10$ 元、$20$ 元。 1. 可选项是比 $31$ 元面值更小的货币,包括 $1$ 元、$5$ 元、$10$ 元、$20$ 元。
2. 从可选项中拿出最大的 $20$ 元,剩余 $31 - 20 = 11$ 元。 2. 从可选项中拿出最大的 $20$ 元,剩余 $31 - 20 = 11$ 元。
@ -53,4 +53,4 @@
!!! tip !!! tip
如果你对数据结构、算法、数组和二分查找等概念仍感到一知半解,请不要担心,继续往下阅读,这本书将引导你迈入数据结构与算法的知识殿堂。 如果你对数据结构、算法、数组和二分查找等概念仍感到一知半解,请继续往下阅读,这本书将引导你迈入数据结构与算法的知识殿堂。

@ -23,7 +23,7 @@
## 数据结构与算法的关系 ## 数据结构与算法的关系
数据结构与算法高度相关、紧密结合,具体表现在下图所示的几个方面。 如下图所示,数据结构与算法高度相关、紧密结合,具体表现以下三个方面。
- 数据结构是算法的基石。数据结构为算法提供了结构化存储的数据,以及用于操作数据的方法。 - 数据结构是算法的基石。数据结构为算法提供了结构化存储的数据,以及用于操作数据的方法。
- 算法是数据结构发挥作用的舞台。数据结构本身仅存储数据信息,结合算法才能解决特定问题。 - 算法是数据结构发挥作用的舞台。数据结构本身仅存储数据信息,结合算法才能解决特定问题。

@ -144,7 +144,7 @@ nav:
- 1.1 &nbsp; 算法无处不在: chapter_introduction/algorithms_are_everywhere.md - 1.1 &nbsp; 算法无处不在: chapter_introduction/algorithms_are_everywhere.md
- 1.2 &nbsp; 算法是什么: chapter_introduction/what_is_dsa.md - 1.2 &nbsp; 算法是什么: chapter_introduction/what_is_dsa.md
- 1.3 &nbsp; 小结: chapter_introduction/summary.md - 1.3 &nbsp; 小结: chapter_introduction/summary.md
- 第 2 章 &nbsp; 时空复杂度: - 第 2 章 &nbsp; 复杂度分析:
# [icon: material/timer-sand] # [icon: material/timer-sand]
- chapter_computational_complexity/index.md - chapter_computational_complexity/index.md
- 2.1 &nbsp; 算法效率评估: chapter_computational_complexity/performance_evaluation.md - 2.1 &nbsp; 算法效率评估: chapter_computational_complexity/performance_evaluation.md

Loading…
Cancel
Save