目录
一. 算法效率
二.时间复杂度
2.1 时间复杂度的概念
2.2 大O的渐进表示法
2.3常见时间复杂度计算举例
三. 空间复杂度
一. 算法效率
二.时间复杂度
2.1 时间复杂度的概念
2.2 大O的渐进表示法
// 请计算一下 func1 基本操作执行了多少次?void func1 ( int N ){int count = 0 ;for ( int i = 0 ; i < N ; i ++ ) { //Nfor ( int j = 0 ; j < N ; j ++ ) { //Ncount ++ ;}}for ( int k = 0 ; k < 2 * N ; k ++ ) { //2*Ncount ++ ;}int M = 10 ;while (( M -- ) > 0 ) { //10count ++ ;}System . out . println ( count );}
Func1 执行的基本操作次数 :
使用大O的渐进表示法:
1 、用常数 1 取代运行时间中的所有加法常数。2 、在修改后的运行次数函数中,只保留最高阶项。3 、如果最高阶项存在且不是 1 ,则去除与这个项目相乘的常数。得到的结果就是大 O 阶。
使用大O的渐进表示法以后,Func1的时间复杂度为:
另外有些算法的时间复杂度存在最好、平均和最坏情况:
2.3常见时间复杂度计算举例
// 计算 func2 的时间复杂度?void func2 ( int N ) {int count = 0 ;for ( int k = 0 ; k < 2 * N ; k ++ ) { //2*Ncount ++ ;}int M = 10 ;while (( M -- ) > 0 ) { //10count ++ ;}System . out . println ( count );}
基本操作执行了2N+10次,通过推导大O阶方法--->时间复杂度为 O(N)
【实例2】
// 计算 func3 的时间复杂度?void func3 ( int N , int M ) {int count = 0 ;for ( int k = 0 ; k < M ; k ++ ) { //Mcount ++ ;}for ( int k = 0 ; k < N ; k ++ ) { //Ncount ++ ;}System . out . println ( count );}
基本操作执行了M+N次,有两个未知数M和N,时间复杂度为 O(N+M)
【实例3】
// 计算 func4 的时间复杂度?void func4 ( int N ) {int count = 0 ;for ( int k = 0 ; k < 100 ; k ++ ) { //100count ++ ;}System . out . println ( count );}
基本操作执行了100次,通过推导大O阶方法,时间复杂度为 O(1).
【实例4】
// 计算 bubbleSort 的时间复杂度?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)
时间复杂度的计算是要配合逻辑的
正确解法:
第一层循环: 当end = n时, 里层 i 从1到 n-1, 循环了n-1次
第二层循环: 当end = n-1时, 里层i从1到n-2, 循环了n-2次
第三层循环: 当end = n-2时, 里层i从1到n-3,循环了n-3次
...
第n-2层循环: 当end = 3时, 里层i从1到2,循环了2次
第n-1层循环: 当end = 2时, 里层i从1到1,循环了1次
第n层循环: 当end = 1时, 里层循环不进行
所以共循环了(n-1)+(n-2)+(n-3)+...3+2+1=n*(n-1)/2, 通过推导大O阶方法, 时间复杂度为 O(N^2)
【实例5】
// 计算 binarySearch 的时间复杂度?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 ;}
二分查找法的思路: 共N个数
第一次查找: 砍掉一半,剩下N/2个
第二次查找: 再砍掉一半, 剩下N/2^2个
第三次查找: 再砍掉一半, 剩下N/2^3个
...
第X次查找: 再砍掉一半, 剩下N/2^X个
当只剩下一个数时, 就找到了这个数, 所以N/2^X = 1 , 解得X=log(以2为低的)N
(ps: 在算法分析中表示是底数 为2,对数为N,有些地方会写成lgN。)
所以, 通过推导大O阶方法, 时间复杂度为 O(lgN).
// 计算阶乘递归 factorial 的时间复杂度?long factorial ( int N ) {return N < 2 ? N : factorial ( N - 1 ) * N ;}
一个一般的递归的时间复杂度计算公式(不是所有递归都适用):
递归的时间复杂度 = 递归的次数 * 每次递归后执行的次数
上述递归:
递归的次数: 当N<2即N=1时, 结束递归, 每次递归-1, 所以congN开始, 共递归了N次
每次递归后执行的次数:每次递归, 只进行了一次三目运算符的判断, 所以执行的次数=1
所以, N*1=N, 通过推导大O阶方法, 时间复杂度为 O(N).
// 计算斐波那契递归 fibonacci 的时间复杂度?int fibonacci ( int N ) {return N < 2 ? N : fibonacci ( N - 1 ) + fibonacci ( N - 2 );}
还是用上述公式:
递归的时间复杂度 = 递归的次数 * 每次递归后执行的次数
递归的次数:我们画图理解一下
先以F(5)为例:每个F(x)代表一次递归
我们可以看到, 从最上面到最下面, n从5变成了1,也就是一共有五层
第一层有1个数, 第二层有2个数, 第三层有4个数, 我可以得到规律:
第n层有2^(n-1)个数
所以递归的次数为:1+2+4+...+2^(n-1)=2^n +1
每次递归后执行的次数:每次递归, 只进行了一次三目运算符的判断, 所以执行的次数=1
三. 空间复杂度
// 计算 bubbleSort 的空间复杂度?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 ;}}}
// 计算 fibonacci 的空间复杂度?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 ;}
【实例3】
// 计算阶乘递归 Factorial 的空间复杂度?long factorial ( int N ) {return N < 2 ? N : factorial ( N - 1 ) * N ;}
递归调用了N次,开辟了N个栈帧,每个栈帧使用了常数个空间。空间复杂度为O(N)