复杂度分析(上)
为什么需要复杂度分析?
你可能会有疑惑,我把代码跑一遍,通过统计、监控,就能得到算法执行的时间和占用的内存大小。为什么还要做时间、空间
复杂度分析呢?这种分析方法能比我实实在在跑一遍得到的数据更准确吗?首先,可以肯定地说,这种评估算法执行效率地方法
是正确地。很多数据结构和算法书籍还给这种方法起了一个名字,叫事后统计法。但是这种统计方法有非常大地局限性:
-
测试结果非常依赖测试环境:
测试环境中硬件的不同会对测试结果有很大的影响。比如,我们拿同样一段代码,分别用Intel Core i9处理器和i3处理器来
运行,不用说,i9处理器要比i3处理器执行的速度快很多。(虽然有些语言可以模拟测试环境,比如Golang,但是依然会受到硬件的
局限)。 -
测试结果受数据规模的影响很大:
我们就拿排序算法来举一个例子。对于同一个排序算法,待排序数据的有序度不一样,排序的执行时间也会有很大的差别。极端
情况下,如果数据已经是有序的,那排序算法不需要做任何操作,执行时间就会非常短。除此之外,如果测试数据规模太小,测试结果
可能无法真实的反映算法的性能。比如,对于小规模的数据排序,插入排序可能反倒会比快速排序要快。
所以,我们需要一个不用具体的测试数据来测试,就可以粗略的估计算法的执行效率的方法。这也就是我们要讲的时间、空间
复杂度分析方法。
大O复杂度表示法
算法的执行效率,组略的讲,就是算法代码执行的时间。但是,如何在不运行代码的情况下,用“肉眼”得到一段代码的执行时间呢?这
里有段非常简单的代码,求1,2,3…n的累加和。现在,我(作者)就带你一块来估算一下这段代码的执行时间。
func cal(n int) int {
sum := 0
i := 1
for ;i<=n;i++ {
sum += i
}
return sum
}
从CPU角度来看,这段代码的每一行都指向着类似的操作:读数据-运算-写数据。尽管每行代码对应的CPU执行的个数、执行的时间都不
一样,但是,我们这里只是组略估计,所以可以假设每行代码执行的时间都是一样的,为uint_time。在这个假设的基础之上,这段代码的总
执行时间是多少呢?