算法时间复杂度分析
一、算法复杂度
算法是为求解一个问题需要遵循的、被清楚指定的简单指令的集合,简单来说就是解决特定问题求解步骤的描述。对于一个问题,一旦某种算法给定并且是正确的,那么重要的一步,就是确定该算法将需要多少时间或者空间等资源量的问题。如果一个问题的求解算法竟然需要长达一年的时间,那么这种算法就很难有什么用处了,同样,一个需要若干个GB内存的算法在当前大多数机器上也是无法使用的。
算法复杂度分为时间复杂度和空间复杂度
- 时间复杂度是指执行这个算法所需要的计算工作量
- 空间复杂度是指执行这个算法所需要的内存空间
二、时间复杂度
1.时间复杂度的概念
首先分析算法的时间复杂度,使用大O表示法,其核心思想是:所有代码的执行时间与代码的执行次数成正比,可以使用以下公式来表达:
T(n) = O( f(n) )
对该公式的解释如下:
- T(n) 表示算法中语句的执行次数
- n 表示数据规模的大小,数据规模n不同,代码的执行时间T(n)也会随之而改变
- f(n) 算法的基本操作(执行次数最多的那条语句)重复执行的次数
在进行算法分析时,代码的总执行时间T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,记作:T(n)= O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,大O时间复杂度实际上并不具体表示代码的真正执行时间,而是表示代码执行时间随数据规模增长的变化趋势,所以称作算法的渐近时间复杂度,简称为时间复杂度。
2.分析时间复杂度的过程
2.1 计算时间复杂度遵守的守则:
(1)对于一些简单的输入输出语句或赋值语句,近似认为需要O(1)时间,(注意:常数项忽略不计)
(2)对于顺序结构,需要依次执行一系列语句所用的时间可采用大O下"求和法则"
加法原则:总复杂度等于量级最大的代码复杂度,即忽略低阶项,高阶项系数以及常数项
因此,我们只关注循环次数最多的代码
(3)对于选择结构,如if语句,它的主要时间耗费是在执行then字句或else字句所用的时间,需注意的是检验条件也需要O(1)时间
(4)对于循环结构,循环语句的运行时间主要体现在多次迭代中执行循环体以及检验循环条件的时间耗费,一般可用大O下"乘法法则"
乘法原则:嵌套代码的复杂度等于嵌套内外代码的复杂度乘积
(5)对于复杂的算法,可以将它分成几个容易估算的部分,然后利用求和法则和乘法法则技术整个算法的时间复杂度
2.2 计算时间复杂度步骤:
第一步:找出算法中的基本语句(执行次数最多的那条语句),通常是最内层循环的循环体。
第二步:计算基本语句的执行次数的数量级
第三步:将基本语句执行次数的数量级放入大Ο记号中
2.3常见时间复杂度举例
2.3.1 常数阶0(1)
int a=3;
int b=4;
int sum=a+b;
总结:只要代码的执行时间不随n的增大而增大,这样的代码时间复杂度都为O(1),一般情况下,只要代码中不存在循环语句、递归语句,即使代码成千上万,都是常数阶
2.3.2 线性阶O(n)
int sum=0;
for(i=0;i<n;i++){
sum++;
}
2.3.3平方阶O(n^2)
最常见的平方阶算法即为两个循环嵌套的情况
int i;
for(i = 0 ; i < n ; i++){
for(j = 0 ; j < n ; j++){
System.out.println("hello");
}
}
以下这段代码的时间复杂度同样为平方阶
int i;
for(i = 0 ; i < n ; i++){
for(j = i ; j < n ; j++){
System.out.println("hello");
}
}
这段代码的内循环 j 是从i开始的,而不是从0开始,因此它的总执行时间为:n+(n-1)+(n-2)+...+1 =n^2/2+n/2
忽略低阶项,去掉最高阶系数,得出它的时间复杂度为0(n^2)
2.3.4 对数阶O(logn)
int i = 1;
while(i <= n){
i *= 2;
}
这段代码表示:当 x个2相乘大于n时,退出循环,即2^x=n x=log2(n) ,代码执行次数x为log2(n),
2.3.5 线性对数阶 O(nlogn)
for(int i = 0 ; i < n ; i++){
fun (i);
}
void fun(int n){
while(i<n){
i*=2;
}
}
外层循环调用的fun()函数时间复杂度为O(logn),利用乘法原则,代码的时间复杂度为O(nlogn)
2.4常见时间复杂度比较
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < { O(2^n) < O(n!) < O(n^n) }
注意: O(2^n) O(n!) O(n^n) 这三项都是非多项式时间复杂度
当N越来越大时,它们的时间复杂度会急剧增长,因此,算法时间复杂度为以上三者时,算法效率奇低
三、空间复杂度
空间复杂度:一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数。
常见空间复杂度: O(1) O(n)
对于一个算法,其时间复杂度和空间复杂度往往是相互影响的。当追求一个较好的时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的存储空间;反之,当追求一个较好的空间复杂度时,可能会使时间复杂度的性能变差,即可能导致占用较长的运行时间。