算法学习—算法复杂度
- 数据结构和算法本质上是”快“和"省"。所以代码的执行效率是非常重要的度量
- 我们采用时间复杂度和空间复杂度来计算
1. 时间复杂度—大O复杂度表示法
-
我们假设执行上图一行代码的时间为t,通过估算,代码的执行时间T(n)与执行次数成正比,记做:T(n)=O(f(n))
- T(n): 代码执行时间
- n:数据规模
- f(n):每行代码执行次数总和
- O:代码的执行时间与f(n)表达式成正比
- 上面的例子中的T(n)=O(2n+2)
- 当n无限大时,低阶、常量、系统都可以忽略
- 所以T(n)=O(n)
- 所以时间复杂度为O(n),也就是代码执行时间随着数据规模的增加而增长
-
下图中,T(n)=O(n*n),也就是代码执行时间随着数据规模的增加而平方增长,所以时间复杂度为O(n^2),时间复杂度也成为渐进时间复杂度。
2. 计算时间复杂度的技巧
- 计算循环执行次数最多的代码
- 总复杂度=量级最大的复杂度
- 如下图所示:时间复杂度为O(n^2),因为嵌套代码的复杂度等于嵌套内外代码复杂度的乘积(乘法法则)
3. 常见的时间复杂度
O(1)
:- 这种是最简单的,也是最好理解的,就是常量级
- 不是只执行了一行代码,只要代码的执行不随着数据规模(n)的增加而增加,就是常量级
- 在实际应用中,通常使用冗余字段存储来将O(n)变成O(1),比如Redis中有很多这样的操作用来提升访问性能
- 比如:SDS、字典、跳跃表等
O(logn)、O(nlogn)
:- 如下图所示:
- 如果2 =n,x=log n,忽略系数为logn,那么T(n)=O(logn),如果将该代码执行n遍 则时间复杂度记录为:T(n)=O(n*logn),即O(nlogn)
- 快速排序、归并排序的时间复杂度都是O(nlogn)
O(n)
:- 很多线性表的操作都是O(n),这也是最常见的一个时间复杂度
- 比如:数组的插入删除、链表的遍历等
O(m+n)
:- 如下图所示,代码的时间复杂度由两个数据的规模来决定
- m和n是代码的两个数据规模,而且不能确定谁更大,此时代码的复杂度为两段时间复杂度之和,即:T(n)=O(m+n),记作:O(m+n)
O(m*n)
:- 根据乘法法则代码的复杂度为两段时间复杂度之积,即T(n)=O(mn),记作:O(mn),当m==n时,为O(n^2)
- 根据乘法法则代码的复杂度为两段时间复杂度之积,即T(n)=O(mn),记作:O(mn),当m==n时,为O(n^2)
4. 空间复杂度
- 空间复杂度全称是渐进空间复杂度,表示算法的存储空间与数据规模之间的增长关系
- 比如将一个数组拷贝到另一个数组中,就是相当于空间扩大了一倍:T(n)=O(2n),忽略系数。即为:O(n),这是一个非常常见的空间复杂度,比如跳跃表、hashmap的扩容
- 此外还有:O(1),比如原地排序、O(n ) 此种占用空间过大
- 由于现在硬件相对比较便宜,所以在开发中常常会利用空间来换时间,比如缓存技术,典型的数据结构中空间换时间是:跳跃表
- 在实际开发中我们也更关注代码的时间复杂度,而用于执行效率的提升