算法的复杂度
来源:互联网 发布:广通软件怎么样 编辑:程序博客网 时间:2024/06/05 19:17
如何衡量一个算法的好与坏?
答案是:用时间复杂度和空间复杂度来衡量算法的好与坏。
一、时间复杂度
时间复杂度是指当前问题的规模以某种单位从1增加到n时,解决这个问题的算法在执行时所耗费的时间也以某种单位由1增加到T(n),就称此算法的时间复杂度为T(n)。
1、分析方法:
(1)时间复杂度就是函数中基本操作所执行的次数。
(2)一般默认的是最坏时间复杂度,即分析最坏情况下所能执行的次数。
(3)忽略掉常数项及系数,保留对增长因子影响最大的一部分。
(4)关注运行时间的增长趋势,关注函数式中增长最快的表达式,忽略系数。
(5)计算时间复杂度是估算随着n的增长函数执行次数的增长趋势。
(6)递归算法的时间复杂度为:递归总次数 * 每次递归中基本操作所执行的次数。
注意:时间复杂度实际就是一个函数,该函数计算的是执行基本操作的次数。
2、算法的情况分类:
(1)最坏情况:任意输入规模的最大运行次数。(上界)
(2)平均情况:任意输入规模的期望运行次数。
(3)最好情况:任意输入规模的最小运行次数,通常最好情况不会出现。(下界)
注意:在实际中,通常考量的是最坏情况。理由如下:
1)一个算法的最坏情况的运行时间是在任意输入下运行时间的上界。
2. 对于某些算法,最坏的情况出现的较为频繁。
3. 大体上看,平均情况与最坏情况一样差。
二、空间复杂度
空间复杂度是指当前问题的规模以某种单位从1增加到n时,解决这个问题的算法在执行时所占用的存储空间也以某种单位由1增加到S(n),就称此算法的空间复杂度为S(n)。
也就是说:算法的空间复杂度并不是计算实际占用的空间,而是计算整个算法的辅助空间单元的个数,与问题的规模没有关系。算法的空间复杂度S(n)定义为该算法所耗费空间的数量级。
(1)若算法执行时所需要的辅助空间相对于输入数据量n而言是一个常数(S(n)=O(f(n))),则称这个算法的辅助空间为O(1)。
(2)递归算法的空间复杂度计算:递归深度*每次递归所创建的对象个数(辅助空间)。如果每次递归所需的辅助空间是常数,则递归的空间复杂度是 O(N)。
三、算法复杂度的计算
1、递归算法的时间复杂度计算:
递归算法的时间复杂度=递归总次数*每次递归次数。
2、递归算法的空间复杂度计算:
递归算法的空间复杂度=递归深度*每次递归所创建的对象个数。
【例一】
(1)计算折半查找的复杂度(非递归)
# include<stdio.h># include<stdlib.h>int BinerySearch(int *arr, int key, int len){ int left = 0; int right = len - 1; while (left <= right) { int mid = left + (right - left) >> 1; if (key < arr[mid]) { return right = mid - 1; } else if (key > arr[mid]) { return left = mid + 1; } else { return mid; } } return -1;}int main(){ int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; int len = sizeof(arr) / sizeof(arr[0]); int ret = BinerySearch(arr, 6, len); if (ret) { printf("找到了!\n"); } else { printf("没找到!\n"); } system("pause"); return 0;}
查找过程如下:
由上图分析可得出规律:
如果程序要执行M次,那么N/(2^M)=1,则M=log2N(2为底数,N为对数)。因为在最坏的情况下,二分查找需要查找的次数为log2N(2为底数,N为对数)次,即执行的次数,所以二分查找的时间复杂度为O(log2N)。因为在整个程序中,新创建变量个数为len是常数级的,所以二分查找的空间复杂度即就是O(1)。
(2)计算折半查找的复杂度(递归)
# include<stdio.h># include<stdlib.h>int count = 0;int BinarySearch(int arr[], int left, int right, int key){ int mid = left + ((right - left) >> 1); if (left > right)//递归出口 { return -1; } if (key < arr[mid]) { right = mid - 1; BinarySearch(arr, left, right, key); } else if (key > arr[mid]) { left = mid + 1; BinarySearch(arr, left, right, key); } else { return mid; } count++;}int main(){ int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; int ret = BinarySearch(arr, 0, 8, 1); if (ret == -1) { printf("没有找到!\n"); } else { printf("找到了!\n"); } printf("%d\n", ret); system("pause"); return 0;}
查找过程示意如下:
对上图分析可得:
1)假设递归的总次数为M,那么递归的深度也是M,则时间复杂度为O(log2 N)//2为底数,N为对数。
2)因为递归的空间复杂度=递归深度每次创建对象的个数,又因为每次创建对象所需要的辅助空间都是常数级别的,所以递归的空间复杂度=M常数=log2 N*常数,
则空间复杂度为O(log2 N )。
【例2】斐波那契数列的时间复杂度和空间复杂度
斐波那契数列的定义如下:
方法一:时间复杂度为:O(2^N);空间复杂度为:O(N)。
#include<stdio.h>#include<stdlib.h>long long Fibonacci1(int n){return n < 2 ? n : Fibonacci1(n - 1) + Fibonacci1(n - 2);}int main(){long long ret = Fibonacci1(4);printf("%ld\n", ret);system("pause");return 0; }
运行结果为:
其图示如下:
1)从图中可看出,这是非满二叉树。因为时间复杂度的计算是在最坏情况下考量的。所以以满二叉树来计算时间复杂度,即递归的总次数为总节点的个数:2^h-1=2^(N-1)-1,其中,h为递归深度,N为层数(在本例中N为4),则时间复杂度为O(2^N)。
2)因为递归算法的空间复杂度=递归深度*每次递归所创建的对象个数,其中递归深度为h=N-1,每次递归所创建的对象个数为2,则递归算法的空间复杂度=(N-1)*2=O(N)。
方法二(非递归-升级):时间复杂度为:O(n);空间复杂度为:O(n)。
long long *Fibonacci2(int n){long long *array = new long long [n + 1];array[0] = 0;//注意array[1] = 1;for (int i = 2; i < n + 1; i++){array[i] = array[i - 1] + array[i - 2];}return array;}
释:因为循环的基本操作次数是:n+1-2=n+1,辅助空间是:n+1,所以:
1)非递归算法的时间复杂度为:O(n);
2)非递归算法的空间复杂度为:O(n)。
方法三(非递归-再升级):时间复杂度为:O(n);空间复杂度为:O(l)。
#include<stdio.h>#include<stdlib.h>long long Fibonacci2(int n){ if (n == 1 || n == 2) { return n; } int first = 1; int second = 1; long long ret = 0; for (int i = 2; i < n; i++) { ret = first + second; first = second; second = ret; } return ret;}int main(){ long long ret = Fibonacci2(16); printf("%ld\n", ret); system("pause"); return 0; }
释:因为循环的基本次数是:n-2+1=n-1,所用的辅助空间是:常数级别的,所以:
1)非递归算法的时间复杂度为:O(n);
2)非递归算法的空间复杂度为:O(l)。
四、时间复杂度的种类
1、常用的时间复杂度有以下七种,如下图所示:
2、复杂度依次增加:
O(1)常数型 < O(log2 n)对数型 < O(n)线性型 < O(nlog2n)二维型 < O(n^2)平方型 < O(n^3)立方型 < O(2^n)指数型。
- 算法的时间复杂度
- 算法复杂度的计算
- 算法的时间复杂度
- 算法的时间复杂度
- 算法复杂度的计算
- 算法复杂度的计算
- 算法的时间复杂度
- 算法的时间复杂度
- 算法的时间复杂度
- 算法的时间复杂度
- 算法的时间复杂度
- 算法的时间复杂度
- 算法的复杂度
- 算法的时间复杂度
- 算法复杂度的计算
- 算法的复杂度
- 算法复杂度的计算
- 算法的时间复杂度
- WorldWind文件分片及存储目录说明
- Maven使用中央仓库下载慢的解决办法
- LintCode:H-求K数和
- 柱状图js封装插件
- Hadoop+Spark学习
- 算法的复杂度
- 设计模式-工厂方法模式
- 【POJ3041】Asteroids(二分图)
- 2017多校训练Contest5: 1001 Rikka with Candies hdu6085
- Vue键盘事件
- 动态规划专题之石子合并
- [NOIP2012]同余方程
- null强制转任何类型的对象
- Android学习心得(三)关于真机调试后,签名的apk无法安装的问题