复杂度:如何衡量程序运行的效率
-
复杂度是什么
- 复杂度是衡量代码运行效率的重要的度量因素。在介绍复杂度之前,有必要先看一下复杂度和计算机实际任务处理效率的关系,从而了解降低复杂度的必要性。
- 计算机通过一个个程序去执行计算任务,也就是对输入数据进行加工处理,并最终得到结果的过程。每个程序都是由代码构成的
-
怎么衡量复杂度
- 实际衡量时,我们通常会围绕以下2 个维度进行。首先,这段代码消耗的资源是什么。一般而言,代码执行过程中会消耗计算时间和计算空间,那需要衡量的就是时间复杂度和空间复杂度。
- 例子:某个十字路口没有建立立交桥时,所有车辆通过红绿灯分批次行驶通过。当大量汽车同时过路口的时候,就会分别消耗大家的时间。但建了立交桥之后,所有车辆都可以同时通过了,因为立交桥的存在,等于是消耗了空间资源,来换取了时间资源。
-
这段代码对于资源的消耗是多少
- 我们不会关注这段代码对于资源消耗的绝对量,因为不管是时间还是空间,它们的消耗程度都与输入的数据量高度相关,输入数据少时消耗自然就少。为了更客观地衡量消耗程度,我们通常会关注时间或者空间消耗量与输入数据量之间的关系。
-
复杂度是一个关于输入数据量 n 的函数
- 假设你的代码复杂度是 f(n),那么就用个大写字母 O 和括号,把 f(n) 括起来就可以了,即 O(f(n))。例如,O(n) 表示的是,复杂度与计算实例的个数 n 线性相关;O(logn) 表示的是,复杂度与计算实例的个数 n 对数相关。
-
线型相关解释:在向量空间V的一组向量A:
,如果存在不全为零的数 k1, k2, ···,km , 使

则称向量组A是线性相关的 ,否则数 k1, k2, ···,km全为0时,称它是线性无关。
由此定义看出
是否线性相关,就看是否存在一组不全为零的数 k1, k2, ···,km使得上式成立。
即是
看这个齐次线性方程组是否存在非零解,将其系数矩阵化为最简形矩阵,即可求解。此外,当这个齐次线性方程组的系数矩阵是一个方阵时,这个系数矩阵存在行列式为0,即有非零解,从而
线性相关。 -
对数:如果a的x次方等于N(a>0,且a≠1),那么数x叫做以a为底N的对数(logarithm),记作x=log_a N。其中,a叫做对数的底数,N叫做真数
时间复杂度与代码结构的关系
-
O(1): Constant Complexity: Constant 常数复杂度
- 一个顺序结构的代码,时间复杂度是 O(1)。
int n = 1000; System.out.println("n: " + n);
- 一个顺序结构的代码,时间复杂度是 O(1)。
-
O(log n): Logarithmic Complexity: 对数复杂度
- 二分查找,或者更通用地说是采用分而治之的二分策略,时间复杂度都是 O(logn)。
for (int i = 1; i < n; i = i * 2) { System.out.println("i: " + i); }
- 二分查找,或者更通用地说是采用分而治之的二分策略,时间复杂度都是 O(logn)。
-
O(n): Linear Complexity: 线性时间复杂度
- 一个简单的 for 循环,时间复杂度是 O(n)。
- 两个顺序执行的 for 循环,时间复杂度是 O(n)+O(n)=O(2n),其实也是 O(n)。
for (int = 1; i<=n; i++) { System.out.println("i: " + i); }
-
O(n^2): N square Complexity 平⽅
- 两个嵌套的 for 循环,时间复杂度是 O(n²)。
for (int i = 1; i <= n; i++) { for (int j = 1; j <=n; j++) { System.out.println("i: " + i + " and " + j); } }
- 两个嵌套的 for 循环,时间复杂度是 O(n²)。
-
O(n^3): N square Complexity ⽴⽅
- 三个嵌套的 for 循环,时间复杂度是 O(n³)。
-
O(2^n): Exponential Growth 指数。
// Math.pow(2, n):以2为底的n次方 for (int i = 1; i <= Math.pow(2, n); i++){ System.out.println("i: " + i); } -
O(n!): Factorial 阶乘
for (int i = 1; i <= factorial(n); i++){ System.out.println("i:" + i); }

复杂度通常包括时间复杂度和空间复杂度。在具体计算复杂度时需要注意以下几点。
它与具体的常系数无关,O(n) 和 O(2n) 表示的是同样的复杂度。
复杂度相加的时候,选择高者作为结果,也就是说 O(n²)+O(n) 和 O(n²) 表示的是同样的复杂度。
O(1) 也是表示一个特殊复杂度,即任务与算例个数 n 无关。
复杂度细分为时间复杂度和空间复杂度,其中时间复杂度与代码的结构设计高度相关;空间复杂度与代码中数据结构的选择高度相关。会计算一段代码的时间复杂度和空间复杂度,是工程师的基本功。
常用算法复杂度

例子
- 1 + 2 + 3 + … + n (总共累加n次)
- for循环累加,时间复杂度为O(n)
- 求和公式:y = n * (n + 1) / 2,时间复杂度为O(1)
数据结构:将“昂贵”的时间复杂度转换成“廉价”的空间复杂度
- 第一步,暴力解法。在没有任何时间、空间约束下,完成代码任务的开发。
- 第二步,无效操作处理。将代码中的无效计算、无效存储剔除,降低时间或空间复杂度。
- 第三步,时空转换。设计合理数据结构,完成时间复杂度向空间复杂度的转移。
本文深入探讨了代码效率优化的重要性和方法,讲解了时间复杂度与空间复杂度的概念,以及它们与代码结构的关系。通过具体案例分析,介绍了常见算法的复杂度,如O(1)、O(logn)、O(n)、O(n²)等,并讨论了如何通过设计合理的数据结构将时间复杂度转换为更经济的空间复杂度。
复杂度&spm=1001.2101.3001.5002&articleId=107306851&d=1&t=3&u=28a25633c28f4526b0b421ccaf653c68)
4万+

被折叠的 条评论
为什么被折叠?



