时间和空间复杂度的学习
目录
1. 算法效率
1.1 如何衡量一个算法的复杂度
算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源。因此衡量一个算法的好坏,一般是从时间和空间连个维度来衡量的,即空间复杂度和时间复杂度。
1.2算法的复杂度
根据摩尔定律(摩尔定律是由英特尔(Intel)创始人之一戈登·摩尔(Gordon Moore)提出来的。其内容为:当价格不变时,集成电路上可容纳的晶体管数目,约每隔18个月便会增加一倍,性能也将提升一倍。换言之,每一美元所能买到的电脑性能,将每隔18个月翻两倍以上。这一定律揭示了信息技术进)所以我们如今不需要对空间复杂度的特别关注。
2.时间复杂度
2.1 时间复杂度的概念
时间复杂度不去计算我们的程序到底走了多少秒,环境不同,具体运行时间就不同。所以选择去看算法中基本操作执行的次数。
可以看出Func1执行的基本次数:
可以看出是关于f(n)的一个函数并且n越大后面的对整体大小的影响越小,实际中我们计算时间复杂度时,我们其实不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大0的渐进表示法
2.2大0的渐行表示法
算法的时间复杂度存在的三种情况:
最好情况:任意输入规模的最小运行次数(下界)
平均情况:任意输入规模的期望运行次数
最坏情况: 任意输入规模的最大运行次数(上界)
例如:在一个长度为N的数组中搜索一个数据x:
最好情况:1次找到
平均情况:N/2次找到
最坏情况:N次找到
在实际中一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)
2.3 常见时间复杂度计算举例
实例1:
void func3(int N,int M)
{
int count = 0;
for(int k = 0;k < M;++k)
{
++count;
}
for(int k = 0;k < N;++k)
{
++count;
}
printf("%d\n,count)
}
没说明M和N的关系,一般情况下时间复杂度计算时未知数都是用的N,但是也可以是M,K等等其他的。
执行次数的函数是:
M<<N 时 :O(N)
M>>N 时 :O(M)
M和N差不多 时 :O(M+N)
注意:O(1) 不是代表执行次数为1,而是执行次数为常数。
实例2(冒泡排序):
可以看出上面是一个冒泡排序的算法。
精确的执行次数为:F(N)=N*(N-1)/2
时间复杂度:O(N*2)
实例3(二分排序):
注意:算时间复杂度不能只去看是几层循环,而是去看他的思想。
最坏的情况:找不到。假设查找了X次
1*2*2*2*...=N
2^X = N
X=log2N
所以时间复杂度:O(log2N)
在N个数中查找 | 大概查找次数 | |
---|---|---|
1000 | 10 | 2^10=N |
100w | 20 | 2^20=N |
10亿 | 30 | 2^30=N |
实例4(递归):
//计算阶乘递归Fac的时间复杂度
long long Fac(size_N)
{
if(0==N)
{
return 1;
}
return Fac(N-1)*N
}
递归算法:递归次数*每次递归调用的次数
时间复杂度:O(N)
实例5(斐波那契递归):
//计算斐波那契递归fib的时间复杂度
long long Fib(size_t N)
{
if(N<3)
{
return 1:
}
return Fib(N-1)+Fib(N-2);
}
FIB(N) = 2^0+2^1+...+2^(N-1)-X = 2^N - 1 (X为缺少的项,X远小于2^N)
时间复杂度:O(2^N)
可以看出斐波那契数列的递归算法实际上是一个没用的算法,因为太慢了。
3.空间复杂度
3.1空间复杂度的概念
3.2 常见空间复杂度计算举例
实例1(冒泡排序):
可见额外变量为 i 和 end 而且都占用常数个空间,所以:
空间复杂度:O(1)
实例2(斐波那契):
时间复杂度:O(N)
空间复杂度:O(N)
实例3(递归):
//计算阶乘递归Fac的空间复杂度
long long Fac(size_N)
{
if(0==N)
{
return 1;
}
return Fac(N-1)*N
}
空间复杂度:O(N)
实例4(斐波那契递归):
//计算斐波那契递归fib的空间复杂度
long long Fib(size_t N)
{
if(N<3)
{
return 1:
}
return Fib(N-1)+Fib(N-2);
}
时间复杂度:O(2^N)
空间复杂度:O(N)
注: 空间是可以重复利用,不累计的。 而 时间是一去不复返,累积的。
最后,这是我自己学习C语言数据结构之空间和时间复杂度时的笔记,有不正确的地方希望大家指正,大家一起努力。