复杂度包括:时间复杂度和空间复杂度,它是用来衡量算法好坏的工具。
时间复杂度:是一个数学函数,定量描述一个算法的运行时间。算法中的基本操作执行次数,为算法的时间复杂度。
时间复杂度是将一个程序中,所有的基本指令执行的次数全部相加得到的一个函数。
例:
//由分析可知,该程序中数据规模n与基本操作次数的关系为:f(n)=3 * N^2 + 9 * N + 32
public static void func1(int N){ //数据规模:N
//执行1次
int count = 0;
//int i = 0执行1次,i<N执行N次,i++执行N次
for (int i = 0; i < N; i++) {
//int j = 0执行N次,j<N执行N * N次,j++执行N * N次
for (int j = 0; j < N; j++) {
//count++执行N * N次
count++;
}
}
//int k = 0执行1次,k<2*N执行2 * N次,k++执行2 * N次
for (int k = 0; k < 2 * N; k++) {
//count++执行2 * N次
count++;
}
int M = 10;
//while((M--)> 0)执行10次,(M--)> 0执行10次
while((M--)>0){
//count++执行10次
count++;
}
System.out.println(count);
}
由上例可知,在计算数据规模n与基本操作次数的关系f(n)是需要将每条基本指令执行的次数逐一算出,之后进行相加。
但是在数据规模N足够大时,低次数项和最高次项数的系数对整个函数的影响并不大,所以,在数据结构中我们通常使用大O渐近法来表示时间复杂度。
大O渐近法:计算出数据规模n与基本操作次数的关系f(n)后,只保留最高次项,并且将最高次项的系数看为1,得到O(n)形式的函数。
计算时间复杂度的步骤:
1.确定数据规模n;
2.计算基本指令个数与n的关系;
3.保留最高次项并且将其系数化为1。
简易方法:直接找到执行次数最多的一条语句,对它的执行次数进行化简就得到了该程序的时间按复杂度。
注:有时程序中会出现多个数据规模的情况,这种情况下,需要将每个数据规模与其基本操作次数的关系f(n)计算出来之后进行相加。
时间复杂度一般包括:最好情况、最坏情况和平均情况
但是我们最在意的是最坏情况下的时间复杂度,因为最好情况只是个例,并且只会在极特殊的情况下发生,而平均情况则需要考虑到很多因素,所以通常情况下我们便认为每一种情况发生的概率是相等的,这时平均情况下的时间复杂度也与最坏情况下的时间复杂度相同,所以我们经常提到的时间复杂度都是最坏情况下的时间复杂度。
关于最好最坏时间复杂度,用冒泡排序举例:
public static void bubbleSort(int[] array){
int n = array.length;
//外部循环
for (int i = 0; i < n - 1; i++) {
//内部循环
for (int j = 0; j < n - i - 1; j++) {
if(array[j] > array[j+1]){
int tmp = array[j];
array[j] = array[j+1];
array[j+1]=tmp;
}
}
}
}
最好情况:数组是由小到大的有序数组,只需要进行一次外部循环的比较,就可以得到最终结果。时间复杂度为:O(n)
最坏情况:数组是由大到小的有序数组,每一次都需要进行比较和交换,才能得到最终结果。时间复杂度为:O(n^2)
平均情况:数组是乱序的,但是仍然需要每一次的比较。时间复杂度为:O(n^2)
常见的时间复杂度:
O(1) < O(log(n)) < O(n) < O(n * log(n)) < O(n^2) < O(2^n)(最坏的时间复杂度,一般不会用到这种时间复杂度)
时间复杂度为O(log(n))的举例:
//二分查找
public static int binarySearch(int[] array,int target){
int left = 0;
int right = array.length - 1;
while(right > left){
int mid = left + (right - left)/2;
if(target == array[mid]){
return mid;
} else if (target < array[mid]) {
right = mid;
}else{
left = mid + 1;
}
}
return -1;
}
时间复杂度为O(2^n)的举例:
//斐波那契数列的递归算法————时间复杂度O(2^n)
public static int fib(int n){
return n<2 ? n : fib(n - 1) + fib(n - 2);
}
如下图所示:递归函数的调用个数是指数型增长,其时间复杂度是O(2^n)
空间复杂度:是对一个算法在运行过程中临时占用存储空间大小的量度。
空间复杂度计算的不是程序占用了多少个字节的空间,而是计算变量的个数,所以其也并没有很大的意义。
空间复杂度的表示也使用大O渐近法。