复杂度分析

复杂度分析

Posted by bulingfeng on May 1, 2024

复杂度分析

为什么需要复杂度分析

有人说可以通过让代码跑一遍,然后进行统计和监控来得到结果。然后跑多次来进行结果的对比。这种方法叫做事后统计法

这种方法有很严重的缺陷:

  1. 测试结果非常依赖于环境;
  2. 测试结果非常受数据规模的影响;

大O复杂度表示法

不运行代码的情况下,一眼能看出来代码的执行效率。

方法可以把代码运行的每一行代码的时间都认为是时间一样的,暂且当做unit_time。那么以下代码执行了多长时间呢:

1
2
3
4
5
6
7
8
 int cal(int n) {
   int sum = 0;
   int i = 1;
   for (; i <= n; ++i) {
     sum = sum + i;
   }
   return sum;
 }

最后的时间是(2n+2)*unit_time。

1
2
3
4
5
6
7
8
9
10
11
 int cal(int n) {
   int sum = 0;
   int i = 1;
   int j = 1;
   for (; i <= n; ++i) {
     j = 1;
     for (; j <= n; ++j) {
       sum = sum +  i * j;
     }
   }
 }

以上的代码的执行时间为:T(n) = (2n2+2n+3)*unit_time。

而不管是(2n+2)unit_time还是(2n2+2n+3)unit_time。我们都可以把其描述为f(n)的一个函数。

即大O表示O(f(n));更具体的解释为:O(2n+2)和O(2n2+2n+3)。

通常只需要考虑增长最快的因素即可,常量、系数已经变化较小的公式都可以舍去,于是上面的大O表示法可以表示为:T(n) = O(n); T(n) = O(n2)

需要注意的是,这个大O表示法并不直接代表代码运行的时间,它只是代表随着数据规模的变化而代码执行消耗时间的趋势,叫做渐进时间复杂度也叫时间负责度。

快速判断时间复杂度的小技巧:

只关注运行代码随着数据规模增长最快的部分即可。

常见的时间复杂度案例分析

上面的复杂度可以分为两种:多项式量级和非多项式量级。

当数据规模 n 越来越大时,非多项式量级算法的执行时间会急剧增加,求解问题的执行时间会无限增长。所以,非多项式时间复杂度的算法其实是非常低效的算法。

O(1)

O(1)并不是只执行了一行代码,只是常量级的时间复杂度表示方法。如下代码:

1
2
3
 int i = 8;
 int j = 6;
 int sum = i + j;

只要代码的执行时间不随 n 的增大而增长,这样代码的时间复杂度我们都记作 O(1)。或者说,一般情况下,只要算法中不存在循环语句、递归语句,即使有成千上万行的代码,其时间复杂度也是Ο(1)。

O(logn)、O(nlogn)

1
2
3
4
 i=1;
 while (i <= n)  {
   i = i * 2;
 }

其实我们就是这个循环里面的代码执行了几次:x=log2n;

1
2
3
4
5
 i=1;
 while (i <= n)  {
   i = i * 3;
 }
// 这段代码的时间复杂度为 O(log3n)。

不管是以 2 为底、以 3 为底,还是以 10 为底,我们可以把所有对数阶的时间复杂度都记为 O(logn)。

如果一段代码的时间复杂度是 O(logn),我们循环执行 n 遍,时间复杂度就是 O(nlogn) 了;

O(m+n)、O(m*n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int cal(int m, int n) {
  int sum_1 = 0;
  int i = 1;
  for (; i < m; ++i) {
    sum_1 = sum_1 + i;
  }

  int sum_2 = 0;
  int j = 1;
  for (; j < n; ++j) {
    sum_2 = sum_2 + j;
  }

  return sum_1 + sum_2;
}

m 和 n 是表示两个数据规模。我们无法事先评估 m 和 n 谁的量级大,所以我们在表示复杂度的时候,就不能简单地利用加法法则,省略掉其中一个。所以,上面代码的时间复杂度就是 O(m+n)。

针对这种情况,原来的加法法则就不正确了,我们需要将加法规则改为:T1(m) + T2(n) = O(f(m) + g(n));

但是乘法法则继续有效:T1(m)*T2(n) = O(f(m) * f(n))。

更详细的时间复杂度分析

  • 最好情况时间复杂度
  • 最坏情况时间复杂度
  • 平均情况时间复杂度
  • 均摊时间复杂度