数据结构与算法学习-复杂度分析

前言

这一篇笔记主要记录总结了什么是算法复杂度?、为什要作算法复杂度分析?、如何作算法复杂度分析?、经常使用的复杂度级别?以及如何掌握复杂度分析?等问题。算法

算法复杂度分析是什么?

数据结构与算法解决的是如何更省、更快的存储和处理数据的问题。所以就须要一个考量效率和资源消耗的方法,这就是复杂度分析方法。数组

复杂度也叫渐进复杂度,其中包含时间复杂度空间复杂度两个维度,是用来分析算法执行时间(或者占用空间)数据规模的增加关系,能够粗略的表示,越高阶复杂度的算法,执行效率越低。数据结构

为何要作算法复杂度分析?

  1. 统计、监控等过后统计法的局限性:性能

    1. 测试结果高度依赖测试环境
    2. 测试结果受数据规模影响很大
  2. 所以须要一个不用具体的测试数据就能够粗略地估计算法的执行效率的方法。复杂度分析不依赖执行环境、成本低、可操做性强。学习

  3. 掌握复杂度分析,将能写出性能更优的代码。测试

如何作算法复杂度分析?

大 O 复杂度表示法

  • 全部代码的执行时间 T(n) 与每行代码的执行次数 n 成正比。
  • 大 O 时间复杂度实际上并不具体表明代码真正的执行时间,而是表示代码执行时间随数据规模增加变化趋势,叫作渐进时间复杂度,简称时间复杂度
  • 因为公式中的低阶、常量、系数对增加趋势不影响,因此均可以忽略,只须要记录一个最大的量级。

算法复杂度分析法则

  1. 只关注循环执行次数最多的一段代码
  2. 加法法则:总复杂度等于量级最大的那段代码的复杂度
  3. 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积

空间复杂度分析

类比时间复杂度,空间复杂度全称就是渐进空间复杂度,表示算法的存储空间数据规模之间增加关系ui

常见的空间复杂度是:O(1)、O(n)、O(n^2)spa

最好、最坏时间复杂度

对有些代码,其复杂度有不是固定的,有最好、最坏的状况。如数组元素查找,有可能第一次就找到,这个时候算法时间复杂度是常数阶O(1),也有可能须要整个遍历一遍数组才能找到,这个时候是最坏的时间复杂度O(n)。code

  • 最好时间复杂度:在最理想状况下,代码执行的时间复杂度。
  • 最坏时间复杂度:在最糟糕的状况下,代码执行的时间复杂度。

平均状况复杂度

因为最好和最坏状况复杂度对应的都是极端状况下的代码复杂度,发生的几率并不大,因此须要引入平均状况复杂度来更好的表示算法的时间复杂度。cdn

平均状况复杂度 是将各类状况下时间复杂度发生的几率考虑进去,而后计算总体时间复杂度的指望值,因此平均状况复杂度也叫作加权平均时间复杂度或者指望时间复杂度

不少时候,只要使用一个复杂度就能够知足需求了。只有同一块代码在不一样状况下,时间复杂度有量级的差距,才会使用这三种复杂度表示法来区分。

均摊时间复杂度

**适用场景:**对一个数据结构进行一组连续操做中,大部分状况下时间复杂度都很低,只有个别状况下时间复杂度比较高,并且这些操做之间存在先后连贯的时序关系,这个状况下,就能够将这一组操做放在一块儿分析,看可否将较高时间复杂度的那次操做平摊到其余那些时间复杂度较低的操做上。通常在可以应用均摊时间复杂度的场景,均摊时间复杂度就等于最好状况时间复杂度

分析方法:摊还分析

经常使用的算法复杂度级别

  • 非多项式量级

    • O(2^n) 和 O(n!)
    • 当数据规模 n 越大时,非多项式量级的算法的执行时间会急剧增长。因此非多项式量级算法是很是低效的算法,所以要避免使用到。
  • 多项式量级

    • O(1):代码的执行时间不随 n 的增大而增加,这样代码的时间复杂度记作 O(1),通常状况下,若是算法中不存在循环语句、递归语句、无论有多少行代码,其时间复杂度都是 O(1)。
    • O(logn)、O(nlogn):对数阶复杂度。
    • O(m+n)、O(m*n):代码的复杂度由两个数据的规模来决定。
  • 多项式量级算法时间复杂度随数据规模的增加变化趋势曲线

如何掌握复杂度分析

多练习。

课后问题

1. 项目以前都会进行性能测试,再作代码的时间复杂度、空间复杂度分析是否是屡次一举?

**解答:**不是画蛇添足,渐进时间,空间复杂度分析为咱们提供了一个很好的理论分析的方向,成本低,可操做性强,不用具体的测试数据就能够粗略地估计算法的执行效率,有助于写出性能高效的代码,同时培养起算法复杂度分析的思惟,能够和性能测试相辅相成。

2. 分析如下代码的复杂度

// 全局变量,大小为 10 的数组 array,长度 len,下标 i。
int array[] = new int[10]; 
int len = 10;
int i = 0;

// 往数组中添加一个元素
void add(int element) {
   if (i >= len) { // 数组空间不够了
     // 从新申请一个 2 倍大小的数组空间
     int new_array[] = new int[len*2];
     // 把原来 array 数组中的数据依次 copy 到 new_array
     for (int j = 0; j < len; ++j) {
       new_array[j] = array[j];
     }
     // new_array 复制给 array,array 如今大小就是 2 倍 len 了
     array = new_array;
     len = 2 * len;
   }
   // 将 element 放到下标为 i 的位置,下标 i 加一
   array[i] = element;
   ++i;
}
复制代码

解答:

  • 最好时间复杂度:当 i 小于 len 时,代码不执行 if 语句,算法时间复杂度是 O(1),
  • 最坏时间复杂度:当 i >= len 时,代码开始执行 if 语句,里面有个循环操做,因此算法时间复杂度是 O(n)。
  • 平均状况复杂度:进入 if 语句后,数组扩容 2 倍,数组每次插入元素的几率都是 1/len + 1,因此根据加权时间复杂度计算公式为 1 * 1/len + 1 + 1 * 1/len + 1 + ... + len *(1/(len+1)) = 1,因此平均状况复杂度是 O(1)。
  • 均摊时间复杂度:每次 O(len)前都有 len 次 O(1),存在先后连贯的时序关系,因此能够将高时间复杂度的 O(len) 平摊到以前的 len 次上,因此均摊时间复杂度是 O(1)。

分享我的技术学习记录和跑步马拉松训练比赛、读书笔记等内容,感兴趣的朋友能够关注个人公众号「青争哥哥」。

相关文章
相关标签/搜索