【算法】算法的时间复杂度和空间复杂度
目录结构:
算法的时间复杂度就是估计一个算法所需的时间,算法的空间复杂度就是估计一个算法所需的内存。算法可以以空间换取时间,也可以以时间换空间。比如,需要求出当前年份是否为闰年,可以写个算法进行计算;也可以预先就把近100年的闰年情况加载到内存中,如果是闰年则为1否则为0,这就是以空间换取时间的栗子。通常情况下“复杂度”都是指的是“时间复杂度”,因此在这篇文章中,笔者也重点介绍时间复杂度。
1.时间复杂度的定义
时间复杂度:在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间量度,记做T(n)=O(f(n)),它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称为算法的渐进时间复杂度,简称为时间复杂度。可以将算法的时间复杂度理解循环次数。
利用大写O()来体现算法时间复杂度的计法,称为大O计法。
最优算法:一般情况下,随着输入规模n的增大,T(n)增长最慢的算法为最优。
2.推导大O阶
推导的一般步骤:
- 用常数1取代运算中的所有加法常数
- 在修改后的运行次数中,只保留最高项
- 如果最高项阶存在且不为1,则去除与这个项相乘的参数
接下来举几个栗子:
#include <stdio.h> int main() { printf("Hello, World! \n"); printf("Hello, World! \n"); printf("Hello, World! \n"); printf("Hello, World! \n"); printf("Hello, World! \n"); printf("Hello, World! \n"); return 0; }
在上面的main中,printf一共打印为6次,这里绝不要理解为算法时间复杂度就是O(6),根据推导大O的阶的推导步骤,可以得出算法的时间复杂度为O(1)。
int n=100,sum=0; //1 for(int i=0;i<n;i++){ //n sum+=(i+1); } printf("%d",sum); //1
这个算法求得1到100的和,总的执行次数为n+2(注意:这里只是大致分析,该代码转化为汇编语言的命令行数绝不止这几行),根据推导规则就是O(n)。
接下来介绍一下高斯算法:
int n=100; int sum=0; sum=(1+n)*n/2; printf("%d",sum);
可以看出,高斯算法的效率非常高,无论求的数之和有多大,算法的时间复杂度都为O(1)。
接来是一个对数阶的栗子:
int i=1,n=100; //1 while(i<n){ //x i=i*2; } printf("%d",i); //1
从上面这行代码中,不能直观的观察出while的循环次数,可以假设循环x次,每一次i都乘于2,所以满足这个条件2^x=n;就可以让程序退出循环。推导可以推出x=logn,所以大O阶为:O(logn)。
3.最优、平均、最差时间复杂度
时间复杂度就是循环次数,那么最优时间复杂度就是考虑循环次数最少的复杂度,最差时间复杂度就是循环次数最多的复杂度,平均时间复杂度就是取中间值。最差时间复杂度是讨论一个算法必需要衡量的一个复杂度。通常情况下,出现最优、平均、最差时间复杂度是因为排序的次数和需要排序的元素大小有关。上面的几个栗子中,就只有一个算法复杂度。比如插入排序,快排等算法的循环次数就和元素的大小、排列顺序有关,他们就具有最优、平均、最差时间复杂度。
上面的概念是不是感觉有点空洞,下面笔者举了个案例来帮助理解。
def findElement(arr, ele): length = len(arr) for index in range(length): if arr[index] == ele: return index return -1; arr = [1,2,3,4] target = 10 res = findElement(arr ,target) print(res)
上面的Python脚本,定义了一个 findElement 函数,这个函数会查找指定某个数是否在一个数组中, 如果有这个元素,那么就返回其下表,如果没有就返回 -1 。
首先我们来看时间复杂度吧,相信经过上面的学习,这里对时间复杂度的分析应该就驾轻就熟了,最优的情况就是:每次要找的元素都在数组的第一个位置,所以最优的时间复杂度就是O(1), 而最差的情况就是要找的元素总是在数组最末尾的位置,而 findElement 的查早算法是从第一个元素开始依次遍历所有的元素,所以查早目标元素也就是需要遍历完数组所有的元素,因此最差时间复杂度就是O(n)。
而空间复杂度就比较简单了, findElement 额外分配了一个length变量 和 index 变量,都是integer类型,所使用的空间和 arr的大小 几乎无关联,因此空间复杂度就是O(1)。O(1) 代表了所使用的空间量是一个常数。
4. 常见算法的复杂度图表
下面是常用算法的时间复杂度(最优,最坏,平均)
上面的图表来自 Big-O Algorithm Complexity Chear Sheet (Know Why Complexities!) 。