数据结构与算法之美03

主要来看几种常见的多项式时间复杂度。

1.O(1)

常量级时间复杂度的一种表示方法,并不是指执行了一行代码。比如这段代码,即便有 3 行,它的时间复杂度也是 O(1 ),而不是 O(3) 。

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

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

2.O(logn),O(nlogn)

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

根据我们前面讲的复杂度分析方法,第三行代码是循环执行次数最多的。所以,我们只要能计算出这行代码被执行了多少次,就能知道整段代码的时间复杂度。 从代码中可以看出,变量 i 的值从 1 开始取,每循环一次就乘以 2 。当大于 n 时,循环结束。还记得我们高中学过的等比数列吗?实际上,变量 i 的取值就是一个等比数列。如果我把它一个一个列出来,就应该是这个样子的:

所以,我们只要知道 x 值是多少,就知道这行代码执行的次数了。通过 2^x=n,则 x=log2n ,所以,这段代码的时间复杂度就是 O(log2n) 。 现在,我把代码稍微改下,你再看看,这段代码的时间复杂度是多少?

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

由上得出这段代码的复杂度为O(log3n) 。

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

我们知道,对数之间是可以互相转换的,log3n就等于log32xlog2n,所以O(log3n)=O(C*log2n),其中C=log32是一个常量。基于我们前面的一个理论:在采用大O标记复杂度的时候,可以忽略系数,即O(Cf(n)=O(f(n))。所以,O(log2n)就等于O(log3n)。因此,在对数阶时间复杂度的表示方法里,我们忽略对数的“底”,统一表示为O(logn)。 如果你理解了我前面讲的O(logn),那O(nlogn)就很容易理解了。还记得我们刚讲的乘法法则吗? 如果一段代码的时间复杂度是O(logn),我们循环执行n遍,时间复杂度就是O(nlogn)了。而且,O(nlogn)也是一种非常常见的算法时间复杂度。比如,归并排序、快速排序的时间复杂度都是O(nlogn)。

3.O(m+n),O(m*n)

这种情况代码的复杂度由两个数据的规模来决定。

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)) 。

猜你喜欢

转载自www.cnblogs.com/fenqinearl/p/10852926.html