目录
1. 集合框架
2. 时间复杂度
2.1 时间复杂度和空间复杂度
2.2 时间复杂度的概念
2.3 大O的渐进表示法
2.3.1 精确的时间复杂度表达式
2.3.2 大O渐进表示法的三条规则
2.3.3 时间复杂度的最好、平均与最坏情况
2.4 时间复杂度计算示例
3.空间复杂度
1. 集合框架
Java集合矿机又被称为集合容器,是定义在java.util包下的一组接口和其实现类。
主要表现为将多个元素置于同一个单元中,用于对这些元素进行快速、便捷地存储、检索、管理;
2. 时间复杂度
2.1 时间复杂度和空间复杂度
算法效率分析为两种:第一种是时间效率,第二种是空间效率。时间效率被称为时间复杂度,空间效率被称为空间复杂度。时间复杂度主要衡量算法的运行速度,空间复杂度主要衡量算法的额外空间。
计算机的飞速发展使得计算机的存储容量已经达到了很高的程度,目前衡量一个算法优劣的重要标准是时间复杂度;
2.2 时间复杂度的概念
在计算机科学中,算法的时间复杂度是一个数学函数,它定量地描述了该算法的运行时间。
一个算法所花费的时间与其语句的执行次数成正比例,算法中的基本操作的执行次数就是算法的时间复杂度;
2.3 大O的渐进表示法
2.3.1 精确的时间复杂度表达式
试分析以下代码的时间复杂度:
public static void fun1(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-->0){count++;}}
分析如下:
精确的时间复杂度表达式为:F(N) = N^2 + 2*N + 10;
实际在我们计算时间复杂度时,并不需要精确的执行次数,而只需要大概执行次数,故而引入大O渐进表示法;
2.3.2 大O渐进表示法的三条规则
(括号中的表达式与上表达式简化过程对应)
(1)用常数1取代运行时间中所有的加法常数;(F(N) = N^2 + 2*N + 1)
(2)在修改后的运行次数函数中,只保留最高阶项;(F(N) = N^2 )
(3)如果最高阶项存在且不是1,则去除与这个项目相乘的常数;
(假如表达式为F(N) = 3*N^2 则去掉3,改为F(N) = N^2 )
即大O渐进表示法是将精确的时间复杂度表达式去掉了对结果影响不大的项,更为简洁地表示出了执行次数;
2.3.3 时间复杂度的最好、平均与最坏情况
(1)最坏情况:任意输入规模的最大运行次数(上界)
(2)平均情况:任意输入规模的期望运行次数
(3)最好情况:任意输入规模的最小运行次数(下界)
一般的时间复杂度指的就是最坏情况下的时间复杂度:
如在一个长度为N的数组中查找某个元素,最好情况是1次找到,最坏情况是N次找到,平均情况是N/2次找到,但数组中搜索数据的时间复杂度是O(N)
2.4 时间复杂度计算示例
例1:
public static void fun2(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);}
时间复杂度为O(N);
例2:
public static void fun3(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);}
时间复杂度是O(M+N);
例3:
public static void fun4(int N){int count=0;for(int k=0;k<100;k++){count++;}System.out.println(count);}
时间复杂度是O(1);
例4:(冒泡排序)
public void bubbleSort(int[] array){for(int end=array.length;end>0;end--){boolean sorted = true;for(int i=1;i<end;i++){if(array[i-1]>array[i]){Swap(array,i-1,i);sorted = false;}}if(sorted ==true)break;}}
计算过程如下图:
得知内外循环共执行次数后,保留最高次项,有:时间复杂度为O(N^2);
注:① N代表的是当前问题规模而非程序中的变量;
② 最好情况是数组本身就有序,只需遍历一次,时间复杂度是O(N);
③ 还有稍有区别的冒泡排序写法:
public static void bubble_sort(int[] array){for(int i=0;i<array.length-1;i++){boolean flag = false;for(int j=0;j<array.length-1-i;j++){if(array[j]>array[j+1]){int tmp=array[j];array[j]=array[j+1];array[j+1]=tmp;flag=true;}}if(flag==false){return;}}}
其计算复杂度过程为:
故其时间复杂度也为O(N^2);
例5:(二分查找)
int binarySearch(int[] array, int value) {int begin = 0;int end = array.length - 1;while (begin <= end) {int mid = begin + ((end-begin) / 2);if (array[mid] < value)begin = mid + 1;else if (array[mid] > value)end = mid - 1;elsereturn mid;}return -1;}
计算过程如下:
也可用列举法:当数据个数分别为2,4,8时,需要执行的次数依次为2,3,4,故而有:2^(x-1)=N,即x=logN+1;
有:时间复杂度是O(logN);
例6:计算阶乘递归factorial的时间复杂度:
public static long factorial(int N) {return N < 2 ? N : factorial(N-1) * N;}
三目运算符的时间复杂度为1,执行了N次,即:时间复杂度为O(N);
例7:计算斐波那契递归fibonacci的时间复杂度:
public static int fibonacci(int N) {return N < 2 ? N : fibonacci(N-1)+fibonacci(N-2);}
求时间复杂度过程如下:
根据以上二叉树可知:时间复杂度为 O(2^N);
注:递归的时间复杂度是递归的次数*每次递归后代码的执行次数;
3.空间复杂度
空间复杂度是对一个算法在运行过程中临时占用储存空间大小的量度。
空间复杂度不是程序占用了多少bytes的空间,而计算的是临时变量的个数,该空间不会随着问题规模的增大而增大;
空间复杂度与时间复杂度计算方法类似,也使用大O渐进表示法;
例1:冒泡排序
public void bubbleSort(int[] array){for(int end=array.length; end>0; end--){boolean sorted = true;for(int i=1; i<end; i++){if(array[i-1]>array[i]){Swap(array,i-1,i);sorted = false;}}if(sorted ==true)break;}}
使用了常数个临时变量,空间复杂度为O(1);
例2:斐波那契数列
int[] 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;}
该程序将计算的斐波那契数列计算的每一个数字都放在fibArray数组中,故而空间复杂度为O(N);
例3:
long factorial(int N) {
return N < 2 ? N : factorial(N-1)*N;
}
调用N次则开辟N个栈帧,每个栈帧使用了常数个空间,故空间复杂度为O(N);
例4:
public static int fibonacci(int N) {return N < 2 ? N : fibonacci(N-1)+fibonacci(N-2);}
空间复杂度为O(N);