1. 算法的效率
我们在写一个算法的时候如何判断这个算法的好坏呢?我们主要从效率来分析,而效率包括时间效率和空间效率
时间效率称为时间复杂度,时间复杂度衡量一个算法的运行速度
空间效率称为空间复杂度,空间复杂度衡量一个算法所需要的额外空间
2 时间复杂度
2.1 时间复杂度的概念
在计算机科学中,时间复杂度是一个数学函数,它定量的描述了算法的运行时间,我们把算法中基本的语句执行的次数称为算法的时间复杂度
所谓基本语句在这里指重复执行的语句
2.2 大O渐进表示法
代码基本语句执行次数分析
我们看这样一段代码:
public class Test { public void func1(int N){ int count = 0; for(int i = 0;i < N;i++){ for(int j = 0;j < N;j++){ count++; } } for(int k = 0;k < 2*N;k++){ count++; } int M = 10; while((M--) > 10){ count++; } } }
分析一下fun1的基本操作执行了多少次,在fun1中,基本操作也就是count++执行了多少次
经分析得:fun1执行的基本操作次数为:F(N) = N^2 + 2N + 10
当N=10时,F(N)=130
当N=100时,F(N) = 10210
当N=1000时,F(N) = 1002010
我们从中发现:当N越大时,2N+10对真实的执行次数影响很小,所以实际中我们计算时间复杂度不一定要精确的执行次数,只需要大概的执行次数,所以这里使用大O渐进法表示时间复杂度
大O渐进法
用常数1取代运行次数中所有的加法常数
在修改后的运行次数函数中只保留最高阶项
如果最高阶项存在且不是1,则去掉与这个项相乘的常数
对上述fun1的时间复杂度用大O渐进法表示:
最高阶项为N^2,去掉2N+10
N^2的系数为1,所以不变
故fun1的时间复杂度为:O(N^2)
算法时间复杂度的三种情况
比如在一个数组中查找某个值为n的数:
当数组中第一个元素为我们要查找的数时,1次就找到,为最好情况
当数组中最后一个元素为我们要查找的数时,N次找到,为最坏情况
当数组中中间的元素为我们要查找的数时,N/2次找到,为平均情况
我们实际关注的是算法的最坏情况,以算法的最坏情况作为该算法的时间复杂度,故数组中查找某个数据的时间复杂度为O(N),N为数组的长度
2.3 常见算法的时间复杂度分析
实例1
public void fun2(int N){ int count = 0; for(int k = 0;k < 2*N;k++){ count++; } int M = 10; while(M > 0){ count++; M--; } }
实例2
public void fun3(int N,int M){ int count = 0; for(int i = 0;i < M;i++){ count++; } for(int j = 0;j < N;j++){ count++; } }
实例3
public void fun4(){ int count = 0; for(int i = 0;i < 100;i++){ count++; } }
冒泡排序的时间复杂度分析
public void bubbleSort(int[] array){ 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]){ int t = array[j]; array[j] = array[j+1]; array[j+1] = t; } } } }
二分查找的时间复杂度分析
public int binarySearch(int[] array,int value){ int begin = 0; int end = array.length-1; while(begin < end){ int mid = begin+((end-begin)>>1); if(array[mid] < value){ begin = mid+1; }else if(array[mid] > value){ end = mid-1; }else { return mid; } } return -1; }
我们经分析出二分查找的时间复杂度为O(lgN),所以在刷题的时候有些题目对算法要求O(lg2),在没有思路的时候就可以考虑用二分法可不可以解决
阶乘递归的时间复杂度分析
public long factorial(int N){ return N < 2 ? N : factorial(N-1)*N; }
斐波那契递归的时间复杂度分析
public long factorial(int N){ return N < 2 ? N : factorial(N-1) + factorial(N-2); }
3. 空间复杂度
空间复杂度是对一个算法在运行的过程中对临时占用内存空间大小的一个量度,空间复杂度的核心就是看算法中是否申请了额外空间,计算规则根时间复杂度的计算规则类似,也使用大O渐进表示法
对于递归算法的空间复杂度:单次递归需要的空间 * 递归的深度
3.1 常见空间复杂度分析
实例1
public void bubbleSort(int[] array){ 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]){ int t = array[j]; array[j] = array[j+1]; array[j+1] = t; } } } }
发现该冒泡排序没有申请额外的空间,故空间复杂度为O(1)
实例2
ong[] fibonacci(int n) { long[] fibArray = new long[n + 1]; fibArray[0] = 0; fibArray[1] = 1; for (int i = 2; i <= n ; i++) { fibArray[i] = fibArray[i - 1] + fibArray [i - 2]; } return fibArray; }
实例3
public int fun(){ int[] arr = new int[10]; arr[0] = 1; return arr[0]; }
说明:只要申请空间的时候指定了大小,不论多大,空间复杂度为O(1)
实例4
public long factorial(int N){ return N < 2 ? N : factorial(N-1)*N; }