1.算法效率
时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需的额外空间。
2.时间复杂度
2.1时间复杂度概念
算法的时间复杂度是一个函数,定量描述了该算法的运行时间。一个算法执行所耗费的时间,从理论上讲是不能算出来的,只能程序跑出来才知道,这很麻烦,所以有了时间复杂度的分析方式。一个算法所花费的时间与其语句执行次数成正比,算法中的基本操作次数为算法的时间复杂度。
实际中我们计算时间复杂度并不一定计算精确执行次数,只需要大概执行次数,用大O的渐进法估算。
2.2大O的渐进表示法
大O:用于描述函数渐进行为的数学符号。
推导方法:
1.用常数1取代所有加法常数。
2.再修改之后的运行次数函数中只保留最高阶项。
3.如果最高阶项存在且不为1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
实例1:
计算fun1的时间复杂度?
void fun1(int n)
{
int count = 0;
for(int i=0;i<2*n;i++)
{
++count;
}
int m = 10;
while(m--)
{
++count;
}
}
此例时间复杂度O(N)
精确算法:2N+10 。常数对此影响不大,计算时间复杂度最高阶项系数为2,则去除常数。其时间复杂度为O(N)
实例2:计算fun2时间复杂度?
void fun2(int N,int M)
{
int count = 0;
for(int i=0;i<M;i++)
{
++count;
}
for(int i = 0;i<N;++k)
{
++count;
}
}
时间复杂度为O(M+N)
【拓】
此时若有条件:
M >> N,时间复杂度为O(M);
N >> M,时间复杂度为O(N);
M与N差不多大,时间复杂度为O(M+N)。
实例3:计算fun3时间复杂度?
void fun3(int n)
{
int count = 0;
for(int i=0;i<100;i++)
{
++count;
}
}
时间复杂度为O(1)
时间复杂度为O(1),表示代表1次,是常数次。
实例4:计算strchr的时间复杂度?
const char * strchr (const char * str,int character);
另外一些算法的时间复杂度存在不同情况:
最坏情况:上界;
平均情况:期望次数(平均次数);
最好:下界。
例如:在一个长度为N的数组搜索一个数据x
最好情况:1次找到。
最差情况:N次找到。
平均情况:N/2次找到。
在实际中当一个算法随着输入不同,时间复杂度不同,一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)。(例外,希尔排序很少出现最坏情况,我们以平均情况计算)
实例5:计算Bubblesort时间复杂度?
void Bubblesort(int * a,int n)
{
assert(a);
for(size_t end = n;end > 0;--end)
{
int exchange = 0;
for(size_t i = 1;i < end;++i)
{
if(a[i-1]>a[i])
{
Swap(&a[i-1],&a[i]);
exchange = 1;
}
}
if(exchange == 0)
break;
}
}
精确:F(N)= (N-1)+(N-2)+...+1 = N*(N-1)/2
时间复杂度O(N^2)
实例6:计算二分查找的时间复杂度?
int BinarySearch(int* a,int n,int x)
{
asssert(a);
int begin = 0;
int end = n;
while(begin < end)
{
int mid = begin + ((end-begin)>>1);
if(a[mid] < x)
begin = mid+1;
else if(a[mid]>x)
end = mid;
else
return mid;
}
return -1;
}
假设查找了x次
1*2*2*2...=n
2^x=n
时间复杂度O(log2 N)
实例7:计算阶乘递归Fac的时间复杂度?
//计算阶乘递归时间复杂度?
long long Fac(size_t N)
{
if(0==N)
return 1;
return Fac(N-1)*N ;
}
时间复杂度O(N)
//计算斐波那契递归时间复杂度?
long long Fib(size_t N)
{
if(N<3)
return 1;
return Fib(N-1)+Fib(N-2);
}
右边一些递归分支会提前结束,缺x次
精确:Fib(N) = 2^0+2^1+2^2+...+2^(N-1)-X ,x远小于2^N可忽略
时间复杂度:O(2^N)
递归算法:递归次数*每次递归调用的次数
3.空间复杂度
空间复杂度也是数学函数表达式,是对一个算法在运行过程中临时额外占用存储空间大小的量度。
空间复杂度不是程序占用了多少byte的空间,因为没有意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则跟时间复杂度类似
注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数运行时显式申请的额外空间来确定。
实例1:计算Bubblesort空间复杂度?
void Bubblesort(int * a,int n)
{
assert(a);
for(size_t end = n;end > 0;--end)
{
int exchange = 0;
for(size_t i = 1;i < end;++i)
{
if(a[i-1]>a[i])
{
Swap(&a[i-1],&a[i]);
exchange = 1;
}
}
if(exchange == 0)
break;
}
}
额外使用了end、i和exchange这三个空间
空间复杂度O(1)
实例2:计算递归的空间复杂度?
//计算阶乘递归空间复杂度?
long long Fac(size_t N)
{
if(0==N)
return 1;
return Fac(N-1)*N ;
}
空间复杂度O(N)
//计算斐波那契递归时间复杂度?
long long Fib(size_t N)
{
if(N<3)
return 1;
return Fib(N-1)+Fib(N-2);
}
空间复杂度:O(N)