算法之美1 - 复杂度分析

复杂度

时间复杂度和空间复杂度


03 | 复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?

一、什么是复杂度分析?

1.数据结构和算法解决是“如何让计算机更快时间、更省空间的解决问题”。

2.因此需从执行时间和占用空间两个维度来评估数据结构和算法的性能。

3.分别用时间复杂度和空间复杂度两个概念来描述性能问题,二者统称为复杂度。

4.复杂度描述的是算法执行时间(或占用空间)与数据规模的增长关系。

二、为什么要进行复杂度分析?

1.和性能测试相比,复杂度分析有不依赖执行环境、成本低、效率高、易操作、指导性强的特点。

2.掌握复杂度分析,将能编写出性能更优的代码,有利于降低系统开发和维护成本。

三、如何进行复杂度分析?

1.大O表示法

1)来源

算法的执行时间与每行代码的执行次数成正比,用T(n) = O(f(n))表示,其中T(n)表示算法执行总时间,f(n)表示每行代码执行总次数,而n往往表示数据的规模

2)特点

以时间复杂度为例,由于时间复杂度描述的是算法执行时间与数据规模的增长变化趋势,所以常量阶、低阶以及系数实际上对这种增长趋势不产决定性影响,所以在做时间复杂度分析时忽略这些项。

2.复杂度分析法则

1)单段代码看高频:比如循环。

2)多段代码取最大:比如一段代码中有单循环和多重循环,那么取多重循环的复杂度。

3)嵌套代码求乘积:比如递归、多重循环等

4)多个规模求加法:比如方法有两个参数控制两个循环的次数,那么这时就取二者复杂度相加。

四、常用的复杂度级别?

多项式阶:随着数据规模的增长,算法的执行时间和空间占用,按照多项式的比例增长。包括,

O(1)(常数阶)、O(logn)(对数阶)、O(n)(线性阶)、O(nlogn)(线性对数阶)、O(n2)(平方阶)、O(n3)(立方阶)

非多项式阶:随着数据规模的增长,算法的执行时间和空间占用暴增,这类算法性能极差。包括,

O(2^n)(指数阶)、O(n!)(阶乘阶)

  • 常量时间复杂度 - 布隆过滤器 / 哈希存储
  • 对数时间复杂度 - 折半查找(二分查找)
  • 线性时间复杂度 - 顺序查找 / 桶排序
  • 对数线性时间复杂度 - 高级排序算法(归并排序、快速排序)
  • 平方时间复杂度 - 简单排序算法(选择排序、插入排序、冒泡排序)
  • 立方时间复杂度 - Floyd算法 / 矩阵乘法运算
  • 几何级数时间复杂度 - 汉诺塔
  • 阶乘时间复杂度 - 旅行经销商问题 - NP

五、如何掌握好复杂度分析方法?

复杂度分析关键在于多练,所谓孰能生巧。

六、思考

有人说,我们项目之前都会进行性能测试,再做代码的时间复杂度、空间复杂度分析,是不是多此一举呢?而且,每段代码都分析一下时间复杂度、空间复杂度,是不是很浪费时间呢?你怎么看待这个问题呢?


04 | 复杂度分析(下):浅析最好、最坏、平均、均摊时间复杂度

一、复杂度分析的4个概念

1.最好情况时间复杂度:代码在最理想情况下执行的时间复杂度。

2.最坏情况时间复杂度:代码在最糟糕情况下执行的时间复杂度。

3.平均时间复杂度:用代码在所有情况下执行的次数的加权平均值表示。

4.均摊时间复杂度:在代码执行的所有复杂度情况中绝大部分是低级别的复杂度,个别情况是高级别复杂度且发生具有时序关系时,可以将个别高级别复杂度均摊到低级别复杂度上。基本上均摊结果就等于低级别复杂度。

二、为什么要引入这4个概念?

1.同一段代码在不同情况下时间复杂度会出现量级差异,为了更全面,更准确的描述代码的时间复杂度,所以引入这4个概念。

2.代码复杂度在不同情况下出现量级差别时才需要区别这四种复杂度。大多数情况下,是不需要区别分析它们的。

三、如何分析平均、均摊时间复杂度?

1.平均时间复杂度

代码在不同情况下复杂度出现量级差别,则用代码所有可能情况下执行次数的加权平均值表示。

2.均摊时间复杂度

两个条件满足时使用:1)代码在绝大多数情况下是低级别复杂度,只有极少数情况是高级别复杂度;2)低级别和高级别复杂度出现具有时序规律。均摊结果一般都等于低级别复杂度。

四、思考

我们今天学的几个复杂度分析方法,你都掌握了吗?你可以用今天学习的知识,来分析一下下面这个 add() 函数的时间复杂度。


// 全局变量,大小为 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;
}

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值