数据结构与算法-01时间复杂度分析

时间复杂度分析

一、思考

  • 算法(Algorithm) 是指用来操作数据、解决程序问题的一组方法。对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但在过程中消耗的资源和时间却会有很大的区别。
    就比如拧一个螺母,扳手和钳子都可以胜任,但使用扳手拧螺母肯定效率更高。
  • 那我们该如何去衡量不同算法之间的优劣呢?
  1. 事后统计法
    通过统计、监控,得到算法执行的时间和占用的内存大小最精准,但有非常大的局限性:
    A 测试结果非常依赖测试环境
    B 测试结果受数据规模的影响很大
  2. 事前分析估算
    大家想一下,当我们要实现一个功能的时候,更多的希望快速知道几种解法中的最优解然后去实现,而不是花大力气去把每种解法都做出来再测试得到结果,因为太低效。
    所以我们需要在代码执行前对影响代码效率的因素(如时间、空间复杂度等)做一个评估。因此我们需要通过复杂度分析来决策,下面我们主要讲解面试中最高频的时间复杂度。
    在这里插入图片描述

二、时间复杂度介绍

概念

时间复杂度到底是个啥?我们先用一段代码理解一下:

int sum(int n){
    
    
    int sum = 0; 
    int i = 1; 
    for(;i<=n;++i){
    
    
        sum=sum+i; 
    }
    return sum;
}
  • 这段代码是进行求和运算的程序,计算机执行每行代码的时间几乎是相同的,如果我们用一个变量time表示每行代码执行时间,那sum方法的总用时计算如下:
    • 第2行代码只执行一次用时time
    • 第3行代码只执行一次用时time
    • 第4行因为要循环n次,需要执行n次,用时n*time
    • 第5行因为要循环n次,需要执行n次,用时n*time
  • 最终结果为2n × time+2 × time即2(n+1) × time。换成人话为:代码运行总时间 = 一行代码执行时间 × 一共执行多少行代码

总结

  1. 因为time可以认为是固定的,所以代码运行总时间与代码执行次数成正比。因此只要我们评判出执行次数的多少,就知道了代码运行总时间的多少。我们只要关注代码执行次数就可以分析出程序的优劣。
  2. 当我们将代码运行总时间用T(n)表示,正比的关系用O表示,一共执行多少行代码用fn()表示,就得到了大家常见的公式。这种表示代码时间复杂度的形式就叫做大 O 时间复杂度表示法。

Tn = O(fn())

  1. 注意:因为O并不代表真正的代码执行时间,只代表递增的趋势,所以称为渐进时间复杂度,简称时间复杂度。
  2. 当n很大时,常量/系数/低阶不会影响增长趋势,所以在计算时间复杂度时都会忽略,只记录最大量级,因此上面代码的时间复杂度忽略常数和系数,最终为Tn = O(n)

在这里插入图片描述

三、常见时间复杂度与分析技巧

常见时间复杂度由小到大依次为:Ο(1) < Ο(logn) <Ο(n) < Ο(nlogn) < Ο(n^2) < Ο(n^3) < Ο(2^n) <Ο(n!),时间复杂度越来越大,执行的效率越来越低。下面举例依次讲解

1. 常数阶O(1)

  • 只要是没有循环等复杂结构,代码执行时不会随着变量的增长而增长,那无论代码多长时间复杂度就都是O(1),如:
int i = 1;
int j = 2;
++i;
j++;
int m = i + j;

2. 线性阶O(n)

  • 「一层循环」,算法需要执行的运算次数用输入大小n的函数表示,即 O(n) 。
for(int i=1;i<=n;i++){
    
    
  System.out.println(i);
}
for(int i=1;i<=n;i++){
    
    
  System.out.println(i);
}
  • 注意:因为代码的for循环不是嵌套关系,执行次数为2 * n 即O(2n),忽略常数为O(n)。

3. 对数阶O(logN)

  • 通过代码来理解下:
int i = 1;
while(i<n){
    
    
    i = i * 2;
}
  • 上面代码我们分析时间复杂度主要是看代码执行了多少次。
  • 假设循环x次后 i>=n ,因为每次都是乘2,于是2的x次方>= n,得到公式 2^x >= n。所以x = log n,即以二为底,n的对数。套用大O表示法,时间复杂度为O(log n)

4. 线性对数阶O(nlogN)

  • nlog n = n * log n,所以相当于把我们上面的案例代码再循环n次,所以下面代码时间复杂度为O(nlog n):
for(m=1; m<n; m++){
    
    
    i = 1;
    while(i<n){
    
    
        i = i * 2;
    }
}

5. 平方阶O(n^2)

  • 一层循环是执行n次,n*n就是两层循环,所以可以结合如下代码理解
for(x=1; i<=n; x++){
    
    
   for(i=1; i<=n; i++){
    
    
       j = i;
       j++;
    }
}

6. 其他情况以此类推

  • 立方阶O(n³)相当于三层n循环,但是O(n³)过大的n都会使得结果变得不现实,同样O(2^n) 和O(n!)等除非是很小的n值,否则哪怕n只是100都是噩梦般的运行时间,所以这种算法时间复杂度一般我们不讨论。

四、时间复杂度分析技巧

通过上面的例子,不知道大家是否已经发现了一定的规律:

  1. 嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
  2. 只关注循环执行次数最多的一段代码,因为公式中的常量、低阶、系数我们都会忽略掉

五、面试题练手

在这里插入图片描述
题目:设某算法的时间复杂度函数的递推方程是T(n)=T(n-1)+n(n为正整数)及T(0)=1,则该算法的时间复杂度为()。

  • A. O(logn)
  • B. O(nlogn)
  • C. O(n)
  • D. O(n^2)

最后答案选D哦~

总结

  • 时间复杂度到这里就结束啦,其实还有更深入的平均时间复杂度、均摊时间复杂度、最好时间复杂度、最坏时间复杂度等等,有时间有需要或许会写一写
  • 巩固自己的同时也希望可以帮到大家~

猜你喜欢

转载自blog.csdn.net/Cathy_2000/article/details/114240964