一.如何衡量算法的好坏
算法效率分析分为两种:第一种是时间效率,第二种是空间效率 。 时间效率被称为时间复杂度,而空间效率被称作 空间复杂度 。 时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间。
二. 时间复杂度
2.1 时间复杂度的概念
时间复杂度的定义:在计算机科学中, 算法的时间复杂度是一个数学函数,它定量描述了该算法的运行时间。
一个算法所花费的时间与其中语句的执行次数成正比例,所以算法中的基本操作的执行次数,为算法的时间复杂度
2.2 时间复杂度的表示
1. 大O渐进表示法
计算一下func1都执行了多少次基本操作?
void func1 ( int N ){int count = 0 ;//此处执行一次赋值操作for ( int i = 0 ; i < N ; i ++ ) {//执行N次自增操作,一次赋值,N+1次比较for ( int j = 0 ; j < N ; j ++ ) {//执行N*N次自增操作,N次赋值,N*(N+1)次比较count ++ ;//执行N*N次自增操作}}
1 、用常数 1 取代运行时间中的所有加法常数。2 、在修改后的运行次数函数中,只保留最高阶项。3 、如果最高阶项存在且不是 1 ,则去除与这个项目相乘的常数。得到的结果就是大 O 阶
void func2 ( int N ) {int count = 0 ;for ( int k = 0 ; k < 2 * N ; k ++ ) {count ++ ;}int M = 10 ;while (( M -- ) > 0 ) {count ++ ;}System . out . println ( count );}
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 ++ ;}System . out . println ( count );}
void func4 ( int N ) {int count = 0 ;for ( int k = 0 ; k < 100 ; k ++ ) {count ++ ;}System . out . println ( count );}
答案分别是O(N),O(M+N),O(1)
2. 时间复杂度的分类
时间复杂度分为三类:
void BubblesSort(int[] array) {
boolean flag=true;
for(int i=0;i<array.length-1;i++) {
for(int j=0;j<array.length-1-i;j++) {
if(array[j]>array[j+1]) {
flag=false;
swap(array[j],array[j+1]);
}
}
if(flag==true)
break;
}
}
最坏情况,就是需要排序的数组时逆序,比如下图
(本人不会动图,辛苦各位将就一下)
i=0时,数字8每次都要和后面的数字比较,每次都比后面的数字大,所以要进行交换,总共执行了n-1次,i=1时,数字7走了数字8的老路,交换次数为n-2次...直到进行了i-1次遍历,数字1被顶到了最前面
所以执行次数为(n-1)+(n-2)+...+2+1=n*(n-1)/2,用大O表示为O(n²)
平均时间复杂度的计算,类似于下面这样一个公式:PI表示情况I出现的概率,tI表示情况I下的时间复杂度,将所有情况的PI和TI相乘,然后进行累加就是平均时间复杂度
对于冒泡排序,它可能出现的情况是十分多的,在这里就不进行计算了
下面来说最好情况,那当然是传入的array数组是有序的了!
在这种情况下,只需要遍历一遍数组,得到flag==true,即每个元素都不需要和后面的元素进行交换,然后break就可以了,时间复杂度为O(n)
既然有三种情况,我们究竟选择哪一种作为时间复杂度呢?
孟子曰:生于忧患死于安乐
这句话对后人是非常有警示作用的,所以我们在计算算法的时间复杂度时,通常情况下会选取最坏情况下的时间复杂度(至于和孟子有没有直接因果关系,不要瞎猜,我是杜撰出来的)
3. 常用的时间复杂度
在算法的学习中,时间复杂度不外乎为n,n²...下面列举常用的时间复杂度
可能有同学会有疑问,为什么对数那里有时候会写lgN,有时会写log2N,有时候直接写logN呢,其实对数的底数不管是2或者10,它们都只差一个常数系数,用大O渐进表示法会直接省略这个系数,所以不需要纠结底数是几
三. 空间复杂度
空间复杂度是对一个算法在运行过程中 临时占用存储空间大小的量度 。空间复杂度不是程序占用了多少 bytes 的空 间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟时间复杂度类似,也 使用大 O 渐进表示法
我们举例帮助理解,下面是一个冒泡排序
void BubblesSort(int[] array) {
boolean flag=true;//此处创建了一个标志性变量flag
for(int i=0;i<array.length-1;i++) {//创建临时变量i
for(int j=0;j<array.length-1-i;j++) {//创建临时变量j
if(array[j]>array[j+1]) {
flag=false;
swap(array[j],array[j+1]);
}
}if(flag==true)
break;
}
}
可以看到,上面的代码创建了flag,i,j三个临时变量,用大O表示空间复杂度是O( 1 )
需要说明的是,此处的要进行排序的数组array并能计算在空间复杂度里,因为它不是BubbleSort方法临时创建的变量
有些时候,我们并不能只计算该方法中的变量数作为空间复杂度,还要计算该方法开辟的栈帧
以N的阶乘为例
long fac ( int N ) {return N < 2 ? N : fac ( N - 1 ) * N ;}
可以看到,每次调用fac方法都需要创建一个临时变量N,这个方法一共执行了N次,所以空间复杂度应为O(N)