算法效率分析分为两种:第一种是时间效率,第二种是空间效率。时间效率被称为时间复杂度,而空间效率被
称作空间复杂度。 时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额
外空间,在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的
迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复
杂度。
 
时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一个
算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但
是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,且不现实。所以才有了时间复杂度这个分析方
式。一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复
杂度。
public class TimeComplexityAndSpaceComplexity {
public static void func1(int N){
int count = 0;
for (int i = 0; i < N ; i++) {
for (int j = 0; j < N ; j++) {
count++;// 第一个就是 这个 count++;,它执行了多少次?
// 两个for嵌套,两个for循环N次,最外围的for循环,每遍历一个数据,嵌套在内部的for循环,就要循环 N 次。
//即 count++; 语句 被循环执行 N*N 次,
// 小技巧,找执行次数最多的语句,你就看哪里有循环就行了。
}
}
for (int k = 0; k < 2 * N ; k++) {
count++;// 这里的 count++; 被执行了 2*N
}
int M = 10;
while ((M--) > 0) {
count++;// 这里count++; 被执行了 10次
}
System.out.println(count);
}
}
经过我们分析,这句代码在程序中 一共被执行了 F(N) = N^2 + 2N + 10
在座的各位,跟着我思考一个问题:
如果我们的 N 越来越大
比如:
N = 10 F(N) = 10^2 + 20 + 10 == 100 + 30 == 130
N = 100 F(N) = 100^2 + 200 + 10 == 10000 + 210 == 10210
N = 1000 F(N) = 1000^2 + 2000 + 10 == 1000000 + 2010 == 1002010
--- ---- ---
---- ---- ---
按照这样想法,后面的 2N + 10,随着N增大,就显得微不足道。
大O符号(Big O notation):是用于描述函数渐进行为的数学符号。
以func1的时间复杂度为例: F(N) = N^2 + 2N + 10
1、用常数1取代运行时间中的所有加法常数。{F(N)= N^2 +2N +1}
2、在修改后的运行次数函数中,只保留最高阶项。{F(N)=N^2}
3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶
假设 使用完方法 1 和 2,把该做的都做了,还剩 3* N^2,
此时,按照方法3的规则去操作(去掉3*),最终的时间复杂度为 O(N^2),
即分析这个代码的时间复杂度,在使用大O的渐进表示法以后
最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数(根据代码的情况给出平均时间复杂度)
最好情况:任意输入规模的最小运行次数(下界)
例如:在一个长度为N数组中搜索一个数据x
最好情况:1次找到
最坏情况:N次找到
平均情况:N/2次找到(可以这么理解,在中间的位置找到的。)
&ensp;
假设数组中有N个元素,现在我们要在数组中查找一个元素,最坏的情况,该元素处在数组末尾,
也就是说 需要遍历整个数组元素。
故:数组中搜索数据时间复杂度为O(N)
public class TimeComplexityAndSpaceComplexity {
public static void func(int N){
int count = 0;
for (int k = 0; k < 2 * N ; k++) {
count++;// 2*N
}
int M = 10;
while ((M--) > 0) {
count++;// 10
}
System.out.println(count);
}
}
时间复杂度为 F(N) = 2*N + 10.使用大0渐进法来表示时间复杂度为 O(N)
public class TimeComplexityAndSpaceComplexity {
public static void func(int N,int M) {
int count = 0;
for (int k = 0; k < M; k++) {
count++;// M
}
for (int k = 0; k < N; k++) {
count++;// N
}
System.out.println(count);
}
}
时间复杂度为 F(N) = N + M.使用大0渐进法来表示时间复杂度为 O(N + M)
&ensp;
public class TimeComplexityAndSpaceComplexity {
public static void func(int N) {
int count = 0;
for (int k = 0; k < 100; k++) {
count++;// 100
}
System.out.println(count);
}
}
时间复杂度为 F(N) = 100.使用大0渐进法来表示时间复杂度为 O(1)
public class TimeComplexityAndSpaceComplexity {
public void bubbleSort(int[] array) {
for (int end = array.length; end > 0; end--) {// 执行 length 次
boolean sorted = true;// 暂不考虑优化(数组已经是有序的, 也就是说 程序在遍历一次判断一下,就结束了)
// 最好情况:时间复杂度为 O(N)
for (int i = 1; i < end; i++) { //length -1 次
if (array[i -1]> array[i]) {
Swap(array, i - 1, i);
sorted = false;
}
}
if (sorted == true) {
break;
}
}
}
}
时间复杂度是考虑最坏情况(每一个元素都需排序),不考虑优化情况和平均情况:
两个for循环嵌套length * (length - 1) == N(N-1) = N^2 - N == N^2
故 冒泡排序的空间复杂度为 O(N^2)
public class TimeComplexityAndSpaceComplexity {
int binarySearch(int[] array, int value) {
int left = 0;
int right = array.length - 1;
while (left <= right) {
int mid = (right + left) / 2;
if (array[mid] < value) {
left = mid + 1;
} else if (array[mid] > value) {
right = mid - 1;
} else {
return mid;
}
}
return -1;
}
} 图1
比如说:我这个递归,递归了N次(N)。每次下去(N) 就是 一个循环,循环是循环了 N 次
那么递归的时间的复杂度 为 N * N^2 = N^3
public class TimeComplexityAndSpaceComplexity {
long factorial(int N) {
return N < 2 ? N : factorial(N-1) * N;
}
}
虽然我们不知道该程序的递归次数,但是程序每次递归下去的时候,遇到是三目运算符。
三目运算符 不是循环,所以 每次递归的次数为1次
再来看看 程序,如果我们求的是 4 的阶乘,也就是 N==4,
N<2,那么只有 N==1时,返回 1(终止递归),然后在看后面 factorial(N-1)
每次递归减一,那么 4,3,2,1 一共 4次
也就是说 递归的次数 为 N 次,
所以 时间的复杂度 = 递归的次数 * 每次递归执行的次数 == N * 1 == N
即 O(N)
public class TimeComplexityAndSpaceComplexity {
int fibonacci(int N) {
return N < 2 ? N : fibonacci(N-1)+fibonacci(N-2);
}
}
还是一样,每次进来就是一个 三目运算符,每次递归执行的次数为1,
再来看 递归的次数
假设我们想求第四个斐波那契数,那么就需要我们去计算 第3 和 第4 个斐波那契数
因为斐波那契数,从第三位数开始,该数 等于 自身前两个数之和。
那么 第3个斐波那契数 ,需要 第1和第2个斐波那契数
第2个斐波那契数 需要 第1个斐波那契数,(因为N<2,是终止条件)
至于,第1个斐波那契数,就不需要再计算了(终止了)
第4个斐波那契数,需要 第3 和 第2个斐波那契数
第3个斐波那契数需要 第2 和 第1 个斐波那契数,第2个斐波那契数,需要第1个斐波那契数,(因为N<2,是终止条件)
至于,第2个斐波那契数,需要第一个斐波那契数 (因为N<2,是终止条件)
见 图 2
斐波那契数的 时间复杂度 见图3
空间复杂度是对一个算法在运行过程中 临时 占用存储空间大小的量度 。空间复杂度不是程序占用了多少bytes的空间
因为这个也没太大意义,所以空间复杂度算的是变量的个数。
空间复杂度计算规则基本跟实践复杂度,类似,也使用大O渐进表示法。
public class TimeComplexityAndSpaceComplexity {
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)
有的人可能会说 sorted 在每次循环的时候,都会创建。
但是请注意 sorted 变量只有1个,你不能说我们循环10次,定义了10个 sorted
每次循环结束 变量空间是会被回收的,循环开始再创建一个。
也就是说 sorted 自始至终都是一个。
至于数组,不算。注意空间复杂度解释的题干中的 “临时” ,你可以理解为另外消耗的,或者说括号里的变量
这么说吧,数组是冒泡排序必须品,而 sorted 只是临时创建的,为了辅助这个程序的运行
所以”临时“的变量个数,只有sorted一个
故 该冒泡排序的空间复杂度为 O(1)
public class TimeComplexityAndSpaceComplexity {
int[] fibonacci(int n) {// 计算斐波那契数的第N项
long[] fibArray = new long[n + 1];
n如果越大,数据就越大,你的结果要存入这个数组里(这里的n+1,是因为下标,自己品)
下面的数组元素,最后都是要存入数组的,也就是说 下面生成的临时变量,都是存入数组的
即元素个数,就是 临时变量的个数,即求斐波那契数 的 空间复杂度 为 O(N+1)
再根据大O渐进法,空间复杂度的最终结果: O(N)
fibArray[0] = 0;
fibArray[1] = 1;
for (int i = 2; i <= n ; i++) {
fibArray[i] = fibArray[i - 1] + fibArray [i - 2];
}
return fibArray;
}
}
public class TimeComplexityAndSpaceComplexity {
long factorial(int N) {
return N < 2 ? N : factorial(N-1)*N;
}
}
递归的空间复杂度有点不一样。
每递归一次,函数就要在栈上开辟一块内存
也就是说递归一次,空间复杂度为 O(1)
递归N次,空间复杂度为 O(N)
即 阶乘递归函数的 空间复杂度为 O(N)
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/DarkAndGrey/article/details/121118562
内容来源于网络,如有侵权,请联系作者删除!