我们在日常生活中如何判断一个算法的好坏呢?那就得先来研究研究算法效率了。
一:算法效率
算法效率分析分为两种:第一种是时间效率(时间复杂度),第二种是空间效率(空间复杂度)。 时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间。
在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度,对于现代计算机,内存已经比较足够,所以对算法效率影响最大的是时间复杂度。
二:时间复杂度
2.1:时间复杂度的概念
算法中的基本操作的执行次数,为算法的时间复杂度。
提问:时间效率的衡量为什么是看基本语句的总的执行次数,而不是看运行时间的长短?
时间的长短会有一定的误差
2.2:大O的渐进表示法
大O符号(Big O notation):是用于描述函数渐进行为的数学符号。
void func1(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++;
}
System.out.println(count);
}
Func1 执行的基本操作次数 :
F(N)=N²+2×N+10
- N = 10 F(N) = 130
- N = 100 F(N) = 10210
- N = 1000 F(N) = 1002010
实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法。
2.3:推导大O阶方法
- 用常数1取代运行时间中的所有加法常数。
- 在修改后的运行次数函数中,只保留最高阶项。
- 如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
使用大O的渐进表示法以后,Func1的时间复杂度为
O(N²)
- N = 10 F(N) = 100
- N = 100 F(N) = 10000
- N = 1000 F(N) = 1000000
通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。
常见时间复杂度:O(1)、 O(logN)、 O(N)、 O(NlogN)、 O(N^2)、 O(N^3)、 O(2^N)
有些算法的时间复杂度存在最好、平均和最坏情况:
最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)
例如:
在数组中找data是否存在,如果存在返回该元素在数组中的下标 ,数组中有N个元素
------做法:将数组遍历一遍,逐个元素与data作比较
最好情况:第一次比较就找到了,一次就够了。
最坏情况:最后一从比较才找到,共比较了N次。
平均情况:=
递归方法时间复杂度求解方式:递归算法单次运行时基本语句执行次数*递归调用总的次数
但是时间复杂度一般关注的是最坏的情况,因为最坏的情况一般是人需要能够接受的底线。
三:空间复杂度
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度 。
核心:对于一般算法只需看算法中是否需要申请额外空间。
常见空间复杂度:O(1) 、O(N)、 O(N^2)
空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法。
递归算法空间复杂度:单次递归需要的空间*递归的深度