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

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

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)。

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

相关推荐
海石2 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石2 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
Jerry4 小时前
LeetCode 151. 反转字符串中的单词
算法
gugucoding5 小时前
31. 【C语言】堆栈与队列的实现
c语言·开发语言·数据结构·链表
ChaoZiLL6 小时前
我的数据结构3——链表(link list)
数据结构·链表
a1117767 小时前
LM 算法迭代过程动画演示(SLAM)
算法
头茬韭菜7 小时前
Context 的生死抉择:四层压缩、截断算法与 Session Memory
算法·ai
Jerry7 小时前
LeetCode 541. 反转字符串 II
算法
Jerry7 小时前
LeetCode 344. 反转字符串
算法
搞科研的小刘选手8 小时前
【香港大学主办&IEEE出版】第六届计算机视觉、应用与算法国际学术会议(CVAA 2026)
算法·计算机视觉·应用·学术会议