目录结构:
contents structure
[-]
算法的时间复杂度就是估计一个算法所需的时间,算法的空间复杂度就是估计一个算法所需的内存。算法可以以空间换取时间,也可以以时间换空间。比如,需要求出当前年份是否为闰年,可以写个算法进行计算;也可以预先就把近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.最优、平均、最差时间复杂度
时间复杂度就是循环次数,那么最优时间复杂度就是考虑循环次数最少的复杂度,最差时间复杂度就是循环次数最多的复杂度,平均时间复杂度就是取中间值。最差时间复杂度是讨论一个算法必需要衡量的一个复杂度。通常情况下,出现最优、平均、最差时间复杂度是因为排序的次数和需要排序的元素大小有关。上面的几个栗子中,就只有一个算法复杂度。比如插入排序,快排等算法的循环次数就和元素的大小、排列顺序有关,他们就具有最优、平均、最差时间复杂度。