这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战算法
刷算法的小伙伴都知道,算法有好有坏,咱们刷算法的最高的一个追求是寻找一个最优解,那么咱们怎么评判一个算法的好坏呢?那就是算法运行时间的长短和占用内存空间的大小 ,对应时间复杂度跟空间复杂度。数组
既然要衡量好坏那么确定要有一种方法,咱们算法中经常使用大O(字母)表示法来衡量算法的时间复杂度(也称渐进时间复杂度)和空间复杂度(也称渐进空间复杂度)。那么大O表示法是什么呢?咱们从时间复杂度角度看,其定义以下:markdown
若存在函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为 不等于零的常数,则称f(n)是T(n)的同数量级函数。记做app
T(n)=O(f(n)),称为O(f(n)),O为算法的渐进时间复杂度,简称为时间复杂度ide
常见的时间/空间复杂度量级有:函数
下面经过经过举一些例子来进行经常使用时间/空间复杂度的判断。post
在 大O符号表示法中,时间复杂度的公式是: T(n) = O( f(n) ),其中f(n) 表示每行代码执行次数之和,而 O 表示正比例关系 。ui
不管代码执行了多少行,只要是没有循环等复杂结构,那这个代码的时间复杂度就都是O(1),如:lua
int i = 1;
int j = 2;
++i;
j++;
int m = i + j;
复制代码
线性阶通常为执行n次的循环或者执行n次递归,如:url
for(i=1; i<=n; ++i)
{
j = i;
j++;
}
复制代码
仍是先来看代码:
int i = 1;
while(i<n)
{
i = i * 2;
}
复制代码
从上面代码能够看到,在while循环里面,每次都将 i 乘以 2,乘完以后,i 距离 n 就愈来愈近了。咱们试着求解一下,假设循环x次以后,i 就大于 2 了,此时这个循环就退出了,也就是说 2 的 x 次方等于 n,那么 x = log2^n 也就是说当循环 log2^n 次之后,这个代码就结束了。所以这个代码的时间复杂度为:O(logn)
线性对数阶O(nlogN) 其实很是容易理解,将时间复杂度为O(logn)的代码循环N遍的话,那么它的时间复杂度就是 n * O(logN),也就是O(nlogN)。
就拿上面的代码加一点修改来举例 :
for(m=1; m<n; m++)
{
i = 1;
while(i<n)
{
i = i * 2;
}
}
复制代码
平方阶O(n²) 就更容易理解了,若是把 O(n) 的代码再嵌套循环一遍,它的时间复杂度就是 O(n²) 了。 举例:
for(x=1; i<=n; x++)
{
for(i=1; i<=n; i++)
{
j = i;
j++;
}
}
复制代码
空间复杂度是对一个算法在运行过程当中临时占用存储空间大小的一个量度,一样反映的是一个趋势,它的公式为:S(n)=O(f(n))。
空间复杂度比较经常使用的有:O(1)、O(n)、O(n²),下面咱们举例看看。
当算法的存储空间大小固定,和输入规模没有直接的关系时,空间复杂度记做O(1)。举例以下:
int i = 1;
int j = 2;
++i;
j++;
int m = i + j;
复制代码
当算法分配的空间是一个线性的集合(如数组),而且集合大小和 输入规模n成正比时,空间复杂度记做O(n)。
举例以下:
void fun(int n){
int[] array = new int[n];
//....
}
复制代码
注意:递归也属于线性空间
当算法分配的空间是一个二维数组集合,而且集合的长度和宽度都 与输入规模n成正比时,空间复杂度记做O(n2)。举例以下:
void fun(int n){
int[][] array = new int[n][n];
//....
}
复制代码
咱们之因此花大力气去评估一个算法的时间复杂度和空间复杂度,其根本缘由是由于计算机的速度和空间是有限。时间复杂度和空间复杂度常常是矛盾的。若是须要下降算法的时间复杂度,则一般须要增长算法的空间复杂度;若是想减小额外空间的使用,则一般会致使算法的时间复杂度增长。 所以咱们须要在它们两个之间做一个取舍,大多数状况下咱们会选择牺牲空间复杂度而成全时间复杂度,由于程序运行期间时间是没法回收的,而空间是能够回收的。
参考:《漫画算法》