算法效率的度量 时间复杂度 空间复杂度

算法效率的度量是通过时间复杂度和空间复杂度来描述的。

1.时间复杂度

一个语句的频度 是指该语句在算法中被重复执行的次数

算法中所有语句的频度之和记为T(n),是该算法问题规模 n 的函数。

时间复杂度主要分析 T(n) 的数量级

算法中基本运算(最深层循环内的语句)的频度与 T(n) 同数量级,因此通常采用算法中基本运算的频度 f(n) 来分析算法的时间复杂度。

算法的时间复杂度记为:

例如 ,取其中随 n 增长最快的项,也就是这一项,将其系数置为1作为时间复杂度的度量,也就是时间复杂度为

其中O的含义是 T(n) 的数量级。这里就不展开定义了。

算法的时间复杂度不仅依赖于问题规模 n ,也取决于待输入数据的性质。比如有些循环有判断条件,所以最深层循环内的语句不一定在所有 n 都执行一遍,实际的频度很可能小于 n 甚至是常数0。

时间复杂度通常考虑三种:最坏、最好和平均时间复杂度。

最坏时间复杂度是指在最坏的情况下,算法的时间复杂度。这个最坏情况可能是要查找一个数,结果在所有待查找中的数全找了一遍结果也没找到。

最好时间复杂度是指在最好的情况下,算法的时间复杂度。这里的最好指的是类似按照某种规则查找一个数,结果在一堆数里面第一次就找到了,后面就不用再找了的情况。

平均时间复杂度是指所有可能输入实例在等概率出现的情况下,算法的期望运行时间。比如要在10个数里面找到一个奇数,那么平均时间复杂度就是这10个数里面一半是奇数一半是偶数时的情况。

一般情况下通常要考虑最坏情况下的时间复杂度,来保证算法的运行时间不会比它更长。

分析一个程序的时间复杂度时,有以下两条规则:

(1)加法规则

(2)乘法规则

常见的渐进时间复杂度为:

这个大小排序还是比较重要的,可以记为"常对幂指阶"。后面有一些例题可以再体会一下时间复杂度怎么看。

2.空间复杂度

算法的空间复杂度 S(n) 定义为该算法所耗费的存储空间,是问题规模 n 的函数。记为:

一个程序在执行时除了需要在存储空间存放本身所用的指令、常数、变量和输入数据外,还需要一些对数据进行操作的工作单元和一些辅助空间。若输入数据所占空间只取决于问题本身。

算法原地工作 是指算法所需的辅助空间为常量,即O(1)。注意这里的常量包括1,2,3,...甚至是100,1000,不管多大,只要是常数就叫原地工作。另一种情况是和算法规模 n 相关的,比如 n 越大需要的辅助空间越大,这种情况就不叫原地工作。

3.时间复杂度例题

下面讲几个经典的复杂度分析的例题。这类题的思路就是找到最基本运算的执行次数,这个执行次数通常和循环联系在一起,考虑的是结束条件。

例1 以下算法的时间复杂度为?

复制代码
void fun(int n){
    int i=1;
    while(i<=n)
        i=i*2;
}

该题为普通循环 。先找到基本运算,也就是while里的 i=i*2 ,这个基本运算的执行次数其实就是这个while循环内执行次数。循环变量从 1 开始,每次都乘 2,直到当前的数大于 n 循环才停止。假设次数是 k ,那么到第 k 次 i 应该为 , 循环结束的条件就是,也就是,这个次数的数量级是,所以时间复杂度就是

例2 以下算法的时间复杂度为?

复制代码
void fun(int n){
    int i=0;
    while(i*i*i<=n)
        i++;
}

该题和上面例1类似,同样是普通循环 。思路和上题完全相同,本质上就是while循环内执行次数,假设执行次数是 k ,到第 k 次 i 就是 k ,循环结束条件是,也就是,所以时间复杂度就是

例3 以下算法的时间复杂度为?

复制代码
x=2;
while(x<n/2)
    x=2*x;

这道题依然是普通循环 。设次数为 k ,那么第 k 次 x 应该为 ,和例1非常相似,循环结束条件就是,也就是,这个数量级还是,时间复杂度为

例4 以下算法的时间复杂度为?

复制代码
for(i=0;i<n;i++){
    for(j=0;j<m;j++)
        a[i][j]=0
}

该题是最基本的嵌套循环 类型,且循环上限不同。外循环执行 n 次,内循环执行 m 次,所以基本运算一共执行 n×m次,时间复杂度就是。注意如果这里 n 或 m 中有一个是常数,不管这个数多大,在最后的时间复杂度中都不会有体现,因为时间复杂度考虑的是数量级,不会有常数。如改为 i<1000 ,j<m ,那最后的时间复杂度就是

例5 以下算法的时间复杂度为?

复制代码
count=0;
for(k=1;k<=n;k*=2){
    for(j=1;j<=n;j++)
        count++;
}

该题是嵌套循环,且是循环和对数混合。此类题的基本运算是嵌套循环里最里面的运算,该题就是count++这一句。要计算执行次数同样也是看循环执行的次数,只不过要考虑两个循环。

在每一次外循环里,内循环执行的次数都是 n ,所以我们就要看外循环有几次,这样基本运算的次数就是几个 n 。恰好外循环和例1中的情况相同,我们知道这个数量级是,再考虑内循环每次执行 n 次,最后的结果就是

循环这里还会有三次循环,或者每次循环变量改变的

例6 以下算法的时间复杂度为?

复制代码
int func(int n) {
    if (n <= 1) return 1;
    return n * func(n - 1);
}

该题考察的是递归类型。递归类型的时间复杂度首先需要明确一个问题:除了递归结束时的那一次(该题对应的就是 n<=1 的时候),其他每次递归其实做了两个操作,一个就是 n 和 func(n-1)相乘的这个乘法,另一个就是递归调用 func(n-1),所以递归式 T(n)=T(n-1)+O(1),这里忘了 T 是什么的往上翻,T(n-1)对应的是递归调用 func(n-1),O(1)对应的就是 n 和 func(n-1)相乘的这个乘法操作。

一种方法是直接展开,这里我们用 c 表示上面递归式的O(1),这个常数时间用什么表示都可以:

T(n) = T(n-1)+c = [T(n-2)+c]+c = ... = T(1) + (n-1)c = c+(n-1)c = nc

注意这里 T(1)对应的就是递归结束 return 1 的情况,所以就是 c 。时间一共是 nc ,最后的时间复杂度就是O(n)。

还可以通过递归树或主定理求时间复杂度,这里就不分析复杂例题了。另外空间复杂度很少有直接分析的题,一般是和算法结合起来考虑,这里就不放例题了。

相关推荐
独自破碎E6 小时前
【二分法】寻找峰值
算法
mit6.8247 小时前
位运算|拆分贪心
算法
ghie90907 小时前
基于MATLAB的TLBO算法优化实现与改进
开发语言·算法·matlab
恋爱绝缘体17 小时前
2020重学C++重构你的C++知识体系
java·开发语言·c++·算法·junit
wuk9987 小时前
VSC优化算法MATLAB实现
开发语言·算法·matlab
Z1Jxxx7 小时前
加密算法加密算法
开发语言·c++·算法
乌萨奇也要立志学C++8 小时前
【洛谷】递归初阶 三道经典递归算法题(汉诺塔 / 占卜 DIY/FBI 树)详解
数据结构·c++·算法
vyuvyucd8 小时前
C++引用:高效编程的别名利器
算法
鱼跃鹰飞8 小时前
Leetcode1891:割绳子
数据结构·算法
️停云️8 小时前
【滑动窗口与双指针】不定长滑动窗口
c++·算法·leetcode·剪枝·哈希