为什么需要复杂度分析?

事后统计法:在通过程序执行通过执行时间分析算法
缺点:

  • 测试结果非常依赖测试环境[电脑cpu]
  • 测试结果受数据规模的影响很大

    大 O 复杂度表示法

    image.png
    在这段代码中 一段代码所需要1 个 unit_time 的执行时间
    而在循环内一段需要n个unit_time时间
    所以=(2n+2)unit_time
    代码的执行时间 T(n) 与每行代码的执行次数成正比
    image.png3+2n+2n^2
    image.png
    T(n)表示代码执行的时间
    n表示数据数据规模大小
    f(n)代码执行的次数
    所以第一个式子T(n)=O(f(2n+2)) 这就是大 O 时间复杂度表示法 这不是表示准确的时间 而是代码执行时间随数据规模增长的变化趋势 而当n数据大的时候 低阶、常量、系数都可以忽略不计 所以*第一个式子可以表示为T(n)=O(f(n))

    时间复杂度分析

    关注循环体

    我们在分析一个算法、一段代码的时间复杂度的时候,也只关注循环执行次数最多的那一段代码就可以了。因为忽略掉公式中的常量、低阶、系数

    加法法则

    总复杂度等于量级最大的那段代码的复杂度
    image.png
    第一个循环体是个常数 所以也是常量
    第二个循环体为n
    第三个循环体n^2
    由于加法法则是取最大的则为O(n^2)

    乘法法则

    嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
    image.png嵌套 第一个o(n)第二个也为o(n) 所以为o(n^2)

    几种常见时间复杂度实例分析

    image.png

    多项式量级

    O(2n) 和 O(n!) 称之为NP非确定性多项式 随着n增大而增大效率低

    非多项式量级

    O(1)

    • 只是常量级级的表示方法
    • 只要代码的时间复杂度不随着n增大 或者说不存在递归循环 不管数据行多大 也是O(1)

image.pngO(1)

O(logn)、O(nlogn)

O(logn)

  1. i=1;
  2. while (i <= n) {
  3. i = i * 2;
  4. }
  5. //前面是for是知道循环的次数 而此处是while
  6. 因为i<=n i是一个等比数列 f(n)=a1*q^n
  7. 2^x=n
  8. x=log2n

image.png
而因为对数中是可以换底的image.pngimage.png而在大O复杂度表示中是不需要计算常数的所以底数的不同不影响时间复杂度都为O(logn**)
O(nlogn)
当一个时间复杂度为logn的程序循环n次就为O(nlogn)**
O(nlogn) 也是一种非常常见的算法时间复杂度。比如,归并排序、快速排序的时间复杂度都是 O(nlogn)。

O(m+n)、O(m*n)

image.png这种情况下m和n的数据量是不可知的 所以加法法则失效 但是乘法法则依旧有效 这里就直接加O(m+n)

空间复杂度分析

时间复杂度的全称是渐进时间复杂度,表示算法的执行时间与数据规模之间的增长关系
空间复杂度全称就是渐进空间复杂度(asymptotic space complexity),表示算法的存储空间与数据规模之间的增长关系


void print(int n) {
  int i = 0;  //分配了一个常数量的内存空间
  int[] a = new int[n]; //分配了一个的 int 类型数组
  for (i; i <n; ++i) {
    a[i] = i * i;
  }

  for (i = n-1; i >= 0; --i) {
    print out a[i]
  }
}

所以为(n)

复杂度分析

四种复杂度情况


// n表示数组array的长度
int find(int[] array, int n, int x) {
  int i = 0;
  int pos = -1;
  for (i; i < n; ++i) {
    if (array[i] == x) {
       pos = i;
       break;
    }
  }
  return pos;
}
在数组中查找指定的值 如果在n=1的时候就找到为O(1) 在n最后的时候找到0(n)
  • 最好情况时间复杂度就是,在最理想的情况下,执行这段代码的时间复杂度。
    • 这个情况就是在第一个的时候找到
  • 最坏情况时间复杂度就是,在最糟糕的情况下,执行这段代码的时间复杂度。
    • 遍历完后找到
  • 平均情况时间复杂度
    • 要查找的变量 x 在数组中的位置,有 n+1 种情况:在数组的 0~n-1 位置中和不在数组中。
    • 查找需要遍历的元素个数累加起来,然后再除以 n+1,就可以得到需要遍历的元素个数的平均值

image.pngimage.png
时间复杂度的大 O 标记法中,可以省略掉系数、低阶、常量,所以,咱们把刚刚这个公式简化之后,得到的平均时间复杂度就是 O(n)。
第二种计算方法
查找的数据在数组和不在数组的概率各为1/2
而在数组内能够找到的概率都为1/n 所以 在数组中能找到的概率为1/2*1/n=1/2n
乘以查找的次数
image.png化简为O(n)

  • 均摊时间复杂度

    
    // array表示一个长度为n的数组
    // 代码中的array.length就等于n
    int[] array = new int[n];
    int count = 0;
    
    void insert(int val) {
    if (count == array.length) {
      int sum = 0;
      for (int i = 0; i < array.length; ++i) {
         sum = sum + array[i];
      }
      array[0] = sum;
      count = 1;
    }
    
    array[count] = val;
    ++count;
    }
    //一个插入数据 如果count为数组长度 则遍历数组且累加将累加和置于数组首位清空数组 如果不为 则直接添加
    

    最好的情况是不是在则为O(1) 最坏的情况为O(n)
    在之前的find中找到数据分为在数组内找到和数组外找到