|
|
|
@ -203,7 +203,7 @@ $$
|
|
|
|
|
|
|
|
|
|
**时间复杂度也存在一定的局限性。** 比如,虽然算法 `A` 和 `C` 的时间复杂度相同,但是实际的运行时间有非常大的差别。再比如,虽然算法 `B` 比 `C` 的时间复杂度要更高,但在输入数据大小 $n$ 比较小时,算法 `B` 是要明显优于算法 `C` 的。即使存在这些问题,计算复杂度仍然是评判算法效率的最有效、最常用方法。
|
|
|
|
|
|
|
|
|
|
## 函数渐进上界
|
|
|
|
|
## 函数渐近上界
|
|
|
|
|
|
|
|
|
|
设算法「计算操作数量」为 $T(n)$ ,其是一个关于输入数据大小 $n$ 的函数。例如,以下算法的操作数量为
|
|
|
|
|
|
|
|
|
@ -284,34 +284,34 @@ $$
|
|
|
|
|
|
|
|
|
|
$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$ ,均有
|
|
|
|
|
$$
|
|
|
|
|
T(n) \leq c \cdot f(n)
|
|
|
|
|
$$
|
|
|
|
|
则可认为 $f(n)$ 给出了 $T(n)$ 的一个渐进上界,记为
|
|
|
|
|
则可认为 $f(n)$ 给出了 $T(n)$ 的一个渐近上界,记为
|
|
|
|
|
$$
|
|
|
|
|
T(n) = O(f(n))
|
|
|
|
|
$$
|
|
|
|
|
|
|
|
|
|
![asymptotic_upper_bound](time_complexity.assets/asymptotic_upper_bound.png)
|
|
|
|
|
|
|
|
|
|
<p align="center"> Fig. 函数的渐进上界 </p>
|
|
|
|
|
<p align="center"> Fig. 函数的渐近上界 </p>
|
|
|
|
|
|
|
|
|
|
本质上看,计算渐进上界就是在找一个函数 $f(n)$ ,**使得在 $n$ 趋向于无穷大时,$T(n)$ 和 $f(n)$ 处于相同的增长级别(仅相差一个常数项 $c$ 的倍数)**。
|
|
|
|
|
本质上看,计算渐近上界就是在找一个函数 $f(n)$ ,**使得在 $n$ 趋向于无穷大时,$T(n)$ 和 $f(n)$ 处于相同的增长级别(仅相差一个常数项 $c$ 的倍数)**。
|
|
|
|
|
|
|
|
|
|
!!! tip
|
|
|
|
|
|
|
|
|
|
渐进上界的数学味儿有点重,如果你感觉没有完全理解,无需担心,因为在实际使用中我们只需要会推算即可,数学意义可以慢慢领悟。
|
|
|
|
|
渐近上界的数学味儿有点重,如果你感觉没有完全理解,无需担心,因为在实际使用中我们只需要会推算即可,数学意义可以慢慢领悟。
|
|
|
|
|
|
|
|
|
|
## 推算方法
|
|
|
|
|
|
|
|
|
|
推算出 $f(n)$ 后,我们就得到时间复杂度 $O(f(n))$ 。那么,如何来确定渐进上界 $f(n)$ 呢?总体分为两步,首先「统计操作数量」,然后「判断渐进上界」。
|
|
|
|
|
推算出 $f(n)$ 后,我们就得到时间复杂度 $O(f(n))$ 。那么,如何来确定渐近上界 $f(n)$ 呢?总体分为两步,首先「统计操作数量」,然后「判断渐近上界」。
|
|
|
|
|
|
|
|
|
|
### 1. 统计操作数量
|
|
|
|
|
|
|
|
|
@ -416,7 +416,7 @@ $$
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. 判断渐进上界
|
|
|
|
|
### 2. 判断渐近上界
|
|
|
|
|
|
|
|
|
|
**时间复杂度由多项式 $T(n)$ 中最高阶的项来决定**。这是因为在 $n$ 趋于无穷大时,最高阶的项将处于主导作用,其它项的影响都可以被忽略。
|
|
|
|
|
|
|
|
|
@ -1330,7 +1330,7 @@ $$
|
|
|
|
|
- 当 `nums = [?, ?, ..., 1]`,即当末尾元素是 $1$ 时,则需完整遍历数组,此时达到 **最差时间复杂度 $O(n)$** ;
|
|
|
|
|
- 当 `nums = [1, ?, ?, ...]` ,即当首个数字为 $1$ 时,无论数组多长都不需要继续遍历,此时达到 **最佳时间复杂度 $\Omega(1)$** ;
|
|
|
|
|
|
|
|
|
|
「函数渐进上界」使用大 $O$ 记号表示,代表「最差时间复杂度」。与之对应,「函数渐进下界」用 $\Omega$ 记号(Omega Notation)来表示,代表「最佳时间复杂度」。
|
|
|
|
|
「函数渐近上界」使用大 $O$ 记号表示,代表「最差时间复杂度」。与之对应,「函数渐近下界」用 $\Omega$ 记号(Omega Notation)来表示,代表「最佳时间复杂度」。
|
|
|
|
|
|
|
|
|
|
=== "Java"
|
|
|
|
|
|
|
|
|
|