|
|
|
@ -1210,11 +1210,7 @@ $$
|
|
|
|
|
|
|
|
|
|
![常数阶、线性阶和平方阶的时间复杂度](time_complexity.assets/time_complexity_constant_linear_quadratic.png)
|
|
|
|
|
|
|
|
|
|
以冒泡排序为例,外层循环执行 $n - 1$ 次,内层循环执行 $n-1, n-2, \cdots, 2, 1$ 次,平均为 $\frac{n}{2}$ 次,因此时间复杂度为 $O(n^2)$ :
|
|
|
|
|
|
|
|
|
|
$$
|
|
|
|
|
O((n - 1) \frac{n}{2}) = O(n^2)
|
|
|
|
|
$$
|
|
|
|
|
以冒泡排序为例,外层循环执行 $n - 1$ 次,内层循环执行 $n-1, n-2, \cdots, 2, 1$ 次,平均为 $n / 2$ 次,因此时间复杂度为 $O((n - 1) n / 2) = O(n^2)$ 。
|
|
|
|
|
|
|
|
|
|
=== "Java"
|
|
|
|
|
|
|
|
|
@ -1596,7 +1592,11 @@ $$
|
|
|
|
|
[class]{}-[func]{log_recur}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
对数阶常出现于基于分治策略的算法中,体现了“一分为多”和“化繁为简”的算法思想。它增长缓慢,是理想的时间复杂度,仅次于常数阶。
|
|
|
|
|
对数阶常出现于基于分治策略的算法中,体现了“一分为多”和“化繁为简”的算法思想。它增长缓慢,是仅次于常数阶的理想的时间复杂度。
|
|
|
|
|
|
|
|
|
|
!!! tip
|
|
|
|
|
|
|
|
|
|
“一分为 $m$”对应的时间复杂度 $O(\log_m n)$ 。我们通常会省略底数 $m$ ,直接将其记为 $O(\log n)$ 。
|
|
|
|
|
|
|
|
|
|
### 线性对数阶 $O(n \log n)$
|
|
|
|
|
|
|
|
|
@ -1762,7 +1762,7 @@ $$
|
|
|
|
|
|
|
|
|
|
![阶乘阶的时间复杂度](time_complexity.assets/time_complexity_factorial.png)
|
|
|
|
|
|
|
|
|
|
请注意,因为 $n! > 2^n$ ,所以阶乘阶比指数阶增长得更快,在 $n$ 较大时也是不可接受的。
|
|
|
|
|
请注意,因为当 $n \geq 4$ 时恒有 $n! > 2^n$ ,所以阶乘阶比指数阶增长得更快,在 $n$ 较大时也是不可接受的。
|
|
|
|
|
|
|
|
|
|
## 最差、最佳、平均时间复杂度
|
|
|
|
|
|
|
|
|
@ -1892,7 +1892,7 @@ $$
|
|
|
|
|
|
|
|
|
|
从上述示例可以看出,最差或最佳时间复杂度只出现于“特殊的数据分布”,这些情况的出现概率可能很小,并不能真实地反映算法运行效率。相比之下,**平均时间复杂度可以体现算法在随机输入数据下的运行效率**,用 $\Theta$ 记号来表示。
|
|
|
|
|
|
|
|
|
|
对于部分算法,我们可以简单地推算出随机数据分布下的平均情况。比如上述示例,由于输入数组是被打乱的,因此元素 $1$ 出现在任意索引的概率都是相等的,那么算法的平均循环次数就是数组长度的一半 $\frac{n}{2}$ ,平均时间复杂度为 $\Theta(\frac{n}{2}) = \Theta(n)$ 。
|
|
|
|
|
对于部分算法,我们可以简单地推算出随机数据分布下的平均情况。比如上述示例,由于输入数组是被打乱的,因此元素 $1$ 出现在任意索引的概率都是相等的,那么算法的平均循环次数就是数组长度的一半 $n / 2$ ,平均时间复杂度为 $\Theta(n / 2) = \Theta(n)$ 。
|
|
|
|
|
|
|
|
|
|
但对于较为复杂的算法,计算平均时间复杂度往往是比较困难的,因为很难分析出在数据分布下的整体数学期望。在这种情况下,我们通常使用最差时间复杂度作为算法效率的评判标准。
|
|
|
|
|
|
|
|
|
|