探秘时间复杂度计算
在了解时间复杂度计算之前,要先明白计算原理,时间复杂度的计算原理就是函数渐近上界理论。
函数渐近上界理论
设算法计算操作数量为T(n),其是一个关于输入数据大小n的函数。假设T(n)是个一次函数,说明时间增长趋势是线性的,因此易得时间复杂度是线性阶。将线性阶的时间复杂度记为 O(n),代表函数T(n)的渐近上界 「asymptotic upper bound」。要推算时间复杂度,本质上是在计算操作数量函数 T(n)的渐近上界。下面我们先来看看函数渐近上界的数学定义。
本质上看,计算渐近上界就是在找一个函数f(n),使得在n趋向于无穷大时,T(n)和f(n)处于相同的增长级别(仅相差一个常数项c的倍数)。
推算方法
由于上述 c⋅f(n)中的常数项c可以取任意大小,因此操作数量T(n)中的各种系数、常数项都可以被忽略。根据此原则,可以总结出以下计数偷懒技巧:
- 跳过数量与 n 无关的操作。因为他们都是 T(n)中的常数项,对时间复杂度不产生影响。
- 省略所有系数。例如,循环 2n次、5n+1次、……,都可以化简记为 n 次,因为 n 前面的系数对时间复杂度也不产生影响。
- 循环嵌套时使用乘法。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别套用上述
1.
和2.
技巧。
最后参考如下表格:
案例汇总
常数阶 O(1)
常数阶的操作数量与输入数据大小 n无关,即不随着n的变化而变化。
/* 常数阶 */
func constant(n int) int {
count := 0
size := 100000
for i := 0; i < size; i++ {
count++
}
return count
}
总结:无论操作数量size
有多大,与数据大小n无关。
线性阶 O(n)
线性阶的操作数量相对输入数据大小成线性级别增长。线性阶常出现于单层循环。
/* 线性阶 */
func linear(n int) int {
count := 0
for i := 0; i < n; i++ {
count++
}
return count
}
总结:遍历操作均为线性阶。
平方阶 O(n2)
平方阶的操作数量相对输入数据大小成平方级别增长。平方阶常出现于嵌套循环,外层循环和内层循环都为 O(n),总体为O(n2)。
/* 平方阶 */
func quadratic(n int) int {
count := 0
// 循环次数与数组长度成平方关系
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
count++
}
}
return count
}
指数阶 O( 2^{n} )
比如细胞分裂就是指数阶。
/* 指数阶(循环实现)*/
func exponential(n int) int {
count, base := 0, 1
// cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1)
for i := 0; i < n; i++ {
for j := 0; j < base; j++ {
count++
}
base *= 2
}
// count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
return count
}
使用递归进行运算。
/* 指数阶(递归实现)*/
func expRecur(n int) int {
if n == 1 {
return 1
}
return expRecur(n-1) + expRecur(n-1) + 1
}
对数阶 O(logn)
对数阶与指数阶正好相反,后者反映“每轮增加到两倍的情况”,而前者反映“每轮缩减到一半的情况”。对数阶仅次于常数阶,时间增长得很慢,是理想的时间复杂度。对数阶常出现于二分查找和分治算法中,体现“一分为多”、“化繁为简”的算法思想。
/* 对数阶(循环实现)*/
func logarithmic(n float64) int {
count := 0
for n > 1 {
n = n / 2
count++
}
return count
}
也同样可以使用递归。
线性对数阶 O(nlogn)
线性对数阶常出现于嵌套循环中,两层循环的时间复杂度分别为 O(logn)和O(n)。主流排序算法的时间复杂度都是 O(nlogn),例如快速排序、归并排序、堆排序等。
/* 线性对数阶 */
func linearLogRecur(n float64) int {
if n <= 1 {
return 1
}
count := linearLogRecur(n/2) +
linearLogRecur(n/2)
for i := 0.0; i < n; i++ {
count++
}
return count
}