01-时间复杂度和空间复杂度

时间复杂度和空间复杂度介绍

大O时间复杂度表示法

我们先来看两个例子:

我们把执行一行代码的时间标为 1

例子一:

int cal(int n) {
   int sum = 0;  // 1
   int i = 1;  // 1 
   for (; i <= n; ++i) {  // n
     sum = sum + i;  // n
   }
   return sum;// 1
 }

对上述执行时间进行相加

2n+3 个执行时间

例子二:

 int cal(int n) {
   int sum = 0;  // 1
   int i = 1; // 1
   int j = 1; // 1
   for (; i <= n; ++i) { // n
     j = 1; // n
     for (; j <= n; ++j) { // n*n
       sum = sum +  i * j; // n*n
     }
   }
 }

我们对上述执行时间进行相加

2n^2+2n+3个执行时间


我们现在看完两个例子了

现在我们来分析一下,可以得出这样一个结论

所有代码的执行时间T(n)与每行代码的执行次数n成正比

最后,我们可以得到一个结论

T(n)=O(f(n))

  • T(n): 是代码的执行时间

  • f(n):表示每行代码的执行次数

  • O():表示代码的执行时间与每行代码的执行次数成正比

上述公式也就是大O时间复杂度表示法

大O时间复杂度实际上并不是具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,所以也叫做渐近时间复杂度,简称时间复杂度


所以我们来看下上面的两个例子

第一个例子的大O时间复杂度: O(2n+3);

第二个例子的大O时间复杂度:O(2n^2+2n+3);

时间复杂度分析

我们该如何去分析时间复杂度呢?

我们来看接下来的方法

注意:分析的时候,忽略掉 公式中的常量,低阶,系数,这并不会影响变化趋势,只需要记录最大阶的量级就行了

  1. 只关注循环执行次数最多的一段代码

     int cal(int n) {
       int sum = 0;
       int i = 1;
       for (; i <= n; ++i) { //n
         sum = sum + i; // n
       }
       return sum;
     }
    

    在这段代码中,执行次数最多的是那个for循环执行的,需要执行n次

    那么它的时间复杂度为 O(n);

  2. 加法法则:总复杂度等于量级最大的那段代码的复杂度

    int cal(int n) {
       int sum_1 = 0; // 1
       int p = 1;  // 1
       for (; p < 100; ++p) { // 100 
         sum_1 = sum_1 + p; //100
       }
     
       int sum_2 = 0;  // 1
       int q = 1; // 1
       for (; q < n; ++q) { // n
         sum_2 = sum_2 + q; //n
       }
     
       int sum_3 = 0; //1
       int i = 1; //1
       int j = 1; //1
       for (; i <= n; ++i) {// n
         j = 1; //1
         for (; j <= n; ++j) { //n*n
           sum_3 = sum_3 +  i * j;//n*n
         }
       }
       return sum_1 + sum_2 + sum_3;//n
     }
    

    这段代码中
    第二个循环代码的时间复杂度为 O(n)

    第三个循环代码的时间复杂度为O(n^2);

    我们取最大量级的时间复杂度,.那就是O(n^2);

  3. 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积

    int cal(int n) {
       int ret = 0; 
       int i = 1;
       for (; i < n; ++i) {
         //下面这段代码调用了f函数
         ret = ret + f(i); // n
       } 
     } 
     
     int f(int n) {  // n*n
      int sum = 0;
      int i = 1;
      for (; i < n; ++i) {
        sum = sum + i;
      } 
      return sum;
     }
    

    我们可以分析一下

    第一段for循环 时间复杂度为O(n);

    第二段fn函数 时间复杂度为O(n);

    因为在第一段for循环中调用了 fn函数

    那么其时间复杂度就是 O(n*n)==>O(n^2)

常见的时间复杂度实例分析

多项式量级(从小到大) 非多项式量级
常量阶O(1) 指数阶O(2^n)
对数阶O(logn) 阶乘阶O(n!)
线性阶O(n)
线性对数阶O(nlogn)
平方阶(n^x)

接下来我们主要分析多项式时间复杂度,因为非多项式时间复杂度,随着数据规模n越来越大的时候,他的执行时间会急剧增加,是非常低效的算法

1.O(1)

就是没有循环,普通执行一行代码的复杂度

这个O(1)是一种方法,即使执行几十行代码,时间复杂度也是O(1)

2. O(logn),O(nlogn)

我们通过例子来分析

 i=1;
 while (i <= n)  {
   i = i * 2;
 }

我们每执行一次循环,i=i*2就会乘一次2,

进行一个运算可以得到

(1) 2^1

(2) 2^2

(3) ....

(n)2^x=n

转换成对数 即log2n

所以时间复杂度为O(logn)

image-20210916103725460


循环执行N遍 就是O(nlogn);

3.O(m+n),O(m*n)

int cal(int m, int n) {
  int sum_1 = 0;
  int i = 1;
  for (; i < m; ++i) {
    sum_1 = sum_1 + i; // m
  }
 
  int sum_2 = 0;
  int j = 1;
  for (; j < n; ++j) {
    sum_2 = sum_2 + j; // n
  }
 
  return sum_1 + sum_2;
}

看上面的代码,这段代码中有两个量级 m和n

因为无法比较两个谁大

所以时间复杂度是O(m+n)

如果是两个循环嵌套的话,那么复杂度就是O(m*n)

空间复杂度分析

空间复杂度表示方法类似于时间复杂度,但是会简单很多

同样都是使用 O进行表示

S(n)=O(f(n)

  • S(n)表示存储空间总和
  • f(n)表示代码所开辟空间
  • O()表示代码开辟的空间与存储空间成正比
void print(int n) {
  int i = 0;
  int[] a = new int[n];
  for (i; i <n; ++i) {
    a[i] = i * i;
  }
 
  for (i = n-1; i >= 0; --i) {
    print out a[i]
  }
}
  1. int i=0 开辟了一块空间
  2. int[] a = new int[n]; 申请了一块n大小的数组存储空间
  3. 其余代码就没有执行开辟空间的操作了
  4. 所以空间复杂度就是O(n)

对比于时间复杂度,空间复杂度真的简单很多,只需观察开辟的空间

如果能用空间换时间,很多人估计都会选择空间换时间

常见的空间复杂度就是 O(1)、O(n)、O(n2 )

posted @ 2021-09-16 11:02  CodeSpirit  阅读(70)  评论(0编辑  收藏  举报