时间与空间复杂度分析

我们用2W1H方法,来介绍简单的时间与空间复杂度分析。并且了解和掌握最好情况时间复杂度、最坏情况时间复杂度、均摊时间复杂度的分析。

What

复杂度包括时间复杂度和空间复杂度,分别用来衡量代码的执行效率、以及对应的内存消耗程度。

Why

不同于性能测试,复杂度分析成本低,易操作,不依赖执行的环境。掌握复杂度的分析,将能编写出性能更优的代码,有利于降低系统开发和维护的成本。此外,还能提升自己分析复杂度的思维和对效率的认知。

How

1、大O表示法:

所有代码的执行时间T(n)与每行代码执行的次数n成正比,即公式:T(n)=O(f(n))。T(n)是要求的算法总执行时间,而f(n)表示每行代码执行的次数,n指数据的规模。

常见的时间复杂度用大O表示法,从低阶到高阶有:O(1)、O(logn)、O(n)、O(nlogn)、O(n^2)。越高阶的算法,执行效率越低。

2、时间复杂度分析原则:

1)看频率最高:
例如出现循环
2)乘积法则:
例如递归、多重嵌套循环。嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
3)相加法则:
总复杂度等于量级最大的那段代码的复杂度,无法评估量级时需要相加表示
4)孰能生巧:
多练多分析

3、举例分析:

O(1):
int sum=0;
int i=0;
int j=0;

由于时间复杂度表示代码执行时间,与数据规模增长的变化趋势,故无论有千百行代码,如果执行时间不随数据n的增大而增大,我们都将其时间复杂度表示为O(1)。

O(logn)、O(nlogn):
int i=1;
while(i<=n){
i*=2;
}

每次循环都有i×2,i的变化符合等比数列,求解有x个2相乘,即x=log2(n)。但由于对数之间是可以相互转换的,所以不论等比的数值怎么变化,我们都可以在对数阶时间复杂度表示中,忽略对数的底,统一表示为O(logn)。

若此段代码在次数为n的循环中执行,那么总的时间复杂度将符合乘积法则,等于O(nlogn)。

O(n)、O(n^2):
int text(int n){
  int sum=0;
  int i=0;
  int j=0;
  for(;i<n;i++){
    j=1;
    for(;j<=n;j++){
      sum+=i*j;
    }
  }
}

分析这段代码,2、3、4行都只执行了一次,5,6行代码各循环执行了n次,7、8行代码各循环执行了n^2次。
故总代码执行的时间复杂度为T(n)=O(2n^2+2n+3),由于复杂度分析是看渐进变化趋势的,当n很大时,我们可以忽略掉常数、系数、低阶的部分。
于是有T(n)=O(n^2)。

O(m+n):
int text(int m,int n){
  int sum=0;
  int i=0;
  int j=0;
  for(;i<m;i++){
      sum+=i;
  }
  for(;j<n;j++){
      sum+=j;
  }
}

根据相加法则,我们无法判断m、n谁的量级更大,只能表示为O(m+n)。

空间复杂度

int i=0;
int[] a= new int[n];

上面这段代码的第一行,申请了一个存储单元的变量i,由于是常量级别我们可以忽略它。于是此段代码的空间复杂度就是数组a开辟内存的长度,即O(n)。

最好情况时间复杂度

一段代码在不同的情况下,时间复杂度是不一样的,例如:

int find(int[] a,int n,int x){
  int i=0;
  int p=-1;
  for(;i<n;i++){
    if(a[i]==x){
      p=i;
      break;
    }
  }
  return p;
}

此代码可以实现的功能是:在数组a中寻找等于x值的元素位置并返回。那么最好情况下,我们只需要循环一次就能找到元素的位置,此时p=0;那么O(1)就是这段代码的最好情况时间复杂度。

最坏情况时间复杂度

紧接上例,如果我们要找的数在数组末尾,此时p=n-1;又或者我们要找的数不在此数组中,此时p=-1。这两种情况下,我们都要遍历整个数组,时间复杂度都是O(n)。那么O(n)就是这段代码的最坏情况时间复杂度。

平均时间复杂度

紧接上例,如果假设x在数组中,和不在数组中的概率相同,都为1/2;那么x出现在数组任意位置处的概率就都是1/2n。那么我们根据0 ~ n-1处的加权平均,可求得一个期望值,即是我们的平均时间复杂度:O((3n+1)/4)。那么这段代码的平均时间复杂度仍为O(n)。

摊还时间复杂度

其实摊还时间复杂度是平均时间复杂度的特例。大致思路是,把耗时多的操作平摊到耗时少的操作上。

如上例中极端情况下,才会有最好情况时间复杂度的出现,为O(1)。跳出所举的例子,假设一般情况都是O(1),每循环n次会出现一个O(n)的操作,那么把它均摊到每次循环操作上。这样均摊下来,这一组连续操作的时间复杂度就是O(1)了。

概括一下:代码执行中的所有复杂度情况中,绝大部分是低级别的复杂度,极个别是高级别复杂度时,(且高低级别复杂度的情况出现有一定时序性)可将高级别复杂度均摊到低级别复杂度上,那么所有情况的复杂度基本就等于低级别复杂度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值