前言:学习算法第一步,复杂度分析。复杂度分析是算法基础中的基础,万丈高楼平地起,要想大楼盖的高,地基必须得打牢。
为什么需要复杂度分析?
我们可以把代码跑一遍,通过统计、监控等,得到算法的执行时间和占用内存。这种统计方法叫做事后统计法。有非常大的局限性。
例如:在不同机器上同样的代码执行的时间不一样。测试数据规模太小,测试结果可能无法真实地反映算法的性能。即:
- 测试结果非常依赖测试环境
- 测试结果受数据规模的影响很大
所以需要一个不用具体的测试数据来测试,就可以粗略的估计算法的执行效率的方法。
大O 复杂度表示法
int cal(int n) {
int sum = 0;
int i = 1;
for (; i <= n; ++i) {
sum = sum + i;
}
return sum;
}
假设每行代码执行的时间是一样的,为unit_time。那么这段代码的总执行时间是多少?
第2、3 行分别需要1 unit_time的执行时间,第4、5 行都运行了n 遍,所以需要2n*unit_time,所以这段代码总的执行时间是(2n+2) * unit_time。可以看出来,所有代码的执行时间与每行代码的执行次数成正比。
T(n) = O(f(n))
-
T(n) 表示代码执行的时间。
-
n 表示数据规模的大小。
-
f(n) 表示每行代码执行的次数总和。因为这是一个公司,所以用f(n) 来表示。
-
O 表示代码的执行时间T(n) 与f(n) 成正比。
注:大O 时间复杂度实际上并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的趋势,所以,也叫做渐进时间复杂度,简称时间复杂度。
公式中的低阶、常量、系数三部分并不左右增长趋势,所以都可以忽略,只需要记录一个最大量级就可以了。
所以我们上面那个例子的T(n) = O(2n+2),即T(n) = O(n)。
怎么做时间复杂度分析
- 只关注循环执行次数最多的一段代码
- 加法法则:总复杂度等于量级最大的那段代码的复杂度
- 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂的的乘积
常见的时间复杂度实例分析
- 常量阶O(1)
- 对数阶O(logn)
- 线性阶O(n)
- 线性对数阶O(nlogn)
- 平方阶O(n2),立方阶O(n3),… K次方阶O(n^k)
- 指数阶O(2^n)
- 阶乘阶O(n!)
其中非多项式量级只有两个指数阶O(2^n)、阶乘阶O(n!)。当数据规模 n 越来越大时,非多项式量级算法的执行时间会急剧增加,求解问题的执行时间会无限增长。所以,非多项式时间复杂度的算法其实是非常低效的算法。
注:什么是单项式,什么是多项式。
由数和字母的积组成的代数式叫做单项式,单独的一个数或一个字母也叫做单项式。由若干个单项式相加组成的代数式叫做多项式。
我个人理解,有不对的地方,可以在下方评论区留言。
就只看上方加粗的时间复杂度,猛地一看他们是单项式的。但是我们在做时间复杂度分析的时候,把低阶、常量、系数这三部分都给忽略掉了,所以实际上上方加粗的时间复杂度,或多或少都带有低阶、常量或者系数,所以他们都是多项式量级的复杂。那两个非多项式量级的怎么理解,因为多项式是由单项式相加组成的代数式叫做多项式,而指数和阶乘并不满足这个条件,所以他们既不是单项式,也不是多项式。
来看几种常见的多项式时间复杂度。
-
O(1)
首先你必须明确一个概念,O(1) 只是常量级时间复杂度的一种表示方法,并不是指只执行了一行代码。比如这段代码,即便有 3 行,它的时间复杂度也是 O(1),而不是 O(3)。
int i = 8; int j = 6; int sum = i + j;
只要代码的执行时间不随 n 的增大而增长,这样代码的时间复杂度我们都记作 O(1)。或者说,一般情况下,只要算法中不存在循环语句、递归语句,即使有成千上万行的代码,其时间复杂度也是Ο(1)。
-
O(logn)、O(nlogn)
-
O(m+n)、O(m*n)
空间复杂度分析
定义:理解了时间复杂度,空间复杂度就非常简单了。类比时间复杂度,空间复杂度的全称是渐进空间复杂度,表示算法的存储空间与数据规模之间的增长关系。
常见的空间复杂度:O(1)、O(n)、O(n2 ),像 O(logn)、O(nlogn) 这样的对数阶复杂度平时都用不到。而且,空间复杂度分析比时间复杂度分析要简单很多。所以,对于空间复杂度,掌握这些内容已经足够了。
不懂就查两个点
- UAT:测试环境,UAT测试:俗称验收测试
- 学习了对数:log
最后留下个思考题,请计算下方代码的时间复杂度,欢迎下方留言
int cal(int n) {
int ret = 0;
int i = 1;
for (; i < n; ++i) {
ret = ret + f(i);
}
}
int f(int n) {
int sum = 0;
int i = 1;
for (; i < n; ++i) {
sum = sum + i;
}
return sum;
}
注:学习极客时间王争老师的《数据结构与算法之美》,自己当做笔记用,顺带做一个小分享,如有侵权,联系我删除。另外强烈推荐这门课程,真的是非常好。