一、复杂度分析

常见复杂度

复杂度是衡量代码运行效率的重要的度量因素。主要包括:时间复杂度,空间复杂度

一、时间复杂度 1.1、大Q复杂度表示法:

Tn = Q(f(n))
Tn:代码执行的时间
n:数据规模的大小
f(n):每行代码执行的次数总和
Q:代码的执行时间 Tn 与 f(n) 表达式成正比

这就是大O时间复杂度表示法。大O时间复杂度实际上并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,所以,也叫作渐进时间复杂度(asymptotic time complexity),简称时间复杂度 。

当n很大时,你可以把它想象成10000、 100000。而公式中的低阶、常量、系数三部分并不左右增长趋势,所以都可以忽略。我们只需要记录一个最大量级就可以了,如果用大O表示法表示刚讲的那两段代码的时间复杂度,就可以记为: T(n) = O(n); T(n) = O(n²)

1.2、分析方法 1、只关注循环执行次数最多的一段代码

大O这种复杂度表示方法只是表示一种变化趋势。我们通常会忽略掉公式中的常量、低阶、系数,只需要记录一个最大阶的量级就可以了。所以, 我们在分析一个算法、一段代码的时间复杂度的时候,也只关注循环执行次数最多的那一段代码就可以了。这段核心代码执行次数的 n 的量级,就是整段要分析代码的时间复杂度。
例:

int cal(int n) { int sum = 0; int i = 1; for (; i <= n; ++i) { sum = sum + i; } return sum; }

其中第2、 3行代码都是常量级的执行时间,与n的大小无关,所以对于复杂度并没有影响。循环执行次数最多的是第4、 5行代码,所以这块代码要重点分析。前面我们也讲过,这两行代码被执行了n次,所以总的时间复杂度就是O(n)。

2、加法法则:总复杂度等于量级最大的那段代码的复杂度

下面这段代码:

int cal(int n) { int sum_1 = 0; int p = 1; for (; p < 100; ++p) { sum_1 = sum_1 + p; } int sum_2 = 0; int q = 1; for (; q < n; ++q) { sum_2 = sum_2 + q; } int sum_3 = 0; int i = 1; int j = 1; for (; i <= n; ++i) { j = 1; for (; j <= n; ++j) { sum_3 = sum_3 + i * j; } } return sum_1 + sum_2 + sum_3; }

这个代码分为三部分,分别是求sum_1、 sum_2、 sum_3。我们可以分别分析每一部分的时间复杂度,然后把它们放到一块儿,再取一个量级最大的作为整段代码的复杂度。

第一段代码循环执行了100次,所以是一个常量的执行时间,跟n的规模无关。即便这段代码循环10000次、 100000次,只要是一个已知的数,跟n无关,照样也是常量级的执行时间。当n无限大的时候,就可以忽略。尽管对代码的执行时间会有很大影响,但是回到时间复杂度的概念来说,它表示的是一个算法执行效率与数据规模增长的变化趋势,所以不管常量的执行时间多大,我们都可以忽略掉。因为它本身对增长趋势并没有影响。

第二段代码和第三段代码的时间复杂度是 O(n) 和 O(n²)。

综合这三段代码的时间复杂度,我们取其中最大的量级。所以,整段代码的时间复杂度就为O(n²)。

也就是说: 总的时间复杂度就等于量级最大的那段代码的时间复杂度。那我们将这个规律抽象成公式就是:

如果 T1(n) = O(f(n)), T2(n) = O(g(n));那么 T(n) = T1(n) + T2(n) = max(O(f(n)), O(g(n))) = O(max(f(n), g(n)))。

3.乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积

如果 T1(n) = O(f(n)), T2(n) = O(g(n));那么 T(n) = T1(n) * T2(n) = O(f(n)) * O(g(n)) = O(f(n) * g(n))。
也就是说,假设T1(n) = O(n), T2(n) = O(n²),则 T1(n) * T2(n) = O(n³)。落实到具体的代码上,我们可以把乘法法则看成是嵌套循环,举个例子:

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; }

我们单独看 cal() 函数。假设 f() 只是一个普通的操作,那第 4~ 6 行的时间复杂度就是, T1(n) = O(n)。但 f() 函数本身不是一个简单的操作,它的时间复杂度是 T2(n) = O(n),所以,整个 cal() 函数的时间复杂度就是, T(n) = T1(n) * T2(n) = O(n*n) = O(n²)。

1.3、几种常见时间复杂度实例分析

常见时间复杂度

对于刚罗列的复杂度量级,我们可以粗略地分为两类:

多项式量级。O(1),O(log(n)),O(n^k)等,因为它的规模n出现在底数的位置。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zydzjg.html