复杂度分析:如何分析、统计算法的执行效率和资源消耗
我们都知道,数据结构与算法本身解决的”快“和”省“的问题,即如何让代码执行的速度更快、占据的存储空间更小,所以执行效率是算法的重要考量标准,那如何衡量算法的执行效率呢?这里就要用到时间、空间复杂度分析。
在日常开发过程中,很多小伙伴可能会把代码跑一遍,通过统计、监控得出算法的执行时间和占用的空间大小,但这种方式有一定的局限性:测试环境的硬件条件、数据规模等等,这些都会影响算法的执行效率,我们需要一个不用具体的测试数据测试,可以粗略估计出算法执行效率的方法,也就是我们要介绍的时间、空间复杂度分析方法。
大O复杂度表示法
这里有段非常简单的代码,求 1,2,3...n 的累加和。现在,我就带你一块来估算一下这段代码的执行时间。
int cal(int n) {
int sum = 0;
int i = 1;
for (; i <= n; ++i) {
sum = sum + i;
}
return sum;
}
我们这里只是粗略估计,所以可以假设每行代码的执行时间都一样,为unit_time。在此基础之上,这段代码的执行时间是多少呢?
第2、3行代码分别需要1个unit_time的执行时间,第4、5行代码是一个从1开始,循环n次的for循环,所以分别需要n * unit_time的执行时间,所以一共需要(2 + 2n) * unit_time的执行时间,尽管我们不知道unit_time的具体值,但我们可以看出来一个规律,所有代码的执行时间T(n)与每行代码的执行次数成正比。
我们可以把这个规律总结成一个公式:T(n) = O(f(n)),其中T(n)代表代码的执行时间,n代表数据规模,f(n)代表代码的执行次数总和,O代表代码的执行时间与执行次数成正比。例子就可以表示为 T(n) = O(2n + 2),这就是大O时间复杂度表示法。
大O时间复杂度实际上并不代表代码的具体执行时间,而是表示代码执行时间与数据规模之间的增长趋势,也叫渐进时间复杂度,简称时间复杂度。由于时间复杂度也可以理解为一种变化趋势,而表达式中的低阶、常量、系数对于变化趋势的影响很小,所以都可以忽略,我们只需要记录一个最大量级即可。
对于时间复杂度的分析,有以下三种方法:
1.只关注循环次数最多的一段代码
如果整个算法只有一段代码,则看代码中有没有循环,如果有,则只关注这段循环代码即可,其量级一定是最大的。
2.加法法则:总复杂度等于量级最大的那段代码的复杂度
如果整个算法有多段代码,则找到算法中有单循环或者多重循环的那段代码,进行比较找到最大量级的那段代码,就是整个算法的时间复杂度。
3.嵌套代码的复杂度等于嵌套内外代码的复杂度乘积
如果算法中有嵌套循环,则该循环的时间复杂度是嵌套内外代码的时间复杂度乘积
常见时间复杂度
空间复杂度分析
空间复杂度全称就是渐进空间复杂度(asymptotic space complexity),表示算法的存储空间与数据规模之间的增长趋势。
我们常见的空间复杂度就是 O(1)、O(n)、O(n2 ),像 O(logn)、O(nlogn) 这样的对数阶复杂度平时都用不到。而且,空间复杂度分析比时间复杂度分析要简单很多。所以,对于空间复杂度,掌握刚我说的这些内容已经足够了。