平均时间复杂度

简述

在数据结构与算法中,“时间复杂度”表示“随着数据规模的增长,他的增长趋势”,而时间复杂度,通过细化,可以分为:最好时间复杂度,最坏时间复杂度,平均时间复杂度,均摊时间复杂度。前两者分析起来较为简单,后两者需要稍微展开一下。

平均时间复杂度

平均时间复杂度,又称之为“加权平均时间复杂度”,“期望时间复杂度”,为什么叫加权呢?因为,通常计算平均时间复杂度,需要将概率考虑进去,也就是,在计算平均时间复杂度的时候,需要一个“加权值”,来真正的计算平均时间复杂度。

我们以代码为例,对平均时间复杂度进行分析:

// n 表示数组 array 的长度
int find(int[] array, int n, int x) {
  int i = 0;
  int pos = -1;
  for (; i < n; ++i) {
    if (array[i] == x) {
       pos = I;
       break;
    }
  }
  return pos;
}

代码很简单,表示在一个数组中,找到 x 这个数,最好复杂度是 O(1), 最坏是 O(n)。

那平均复杂度是怎么计算呢?

先说简单的平均值计算公式:

上面的代码,所有查找 x 的时间相加:1 + 2 + 3 +······ + n + n (这个 n 表示当 x 不存在时遍历 array 需要的次数) , 另外,需要查找的次数为 n + 1 次,那么结果就是:

image.png

代入大 O 表达式,结果是 O(n)。

这个公式表达的是:计算所有可能出现的情况之和,然后除以可能出现的情况的次数。说白了,这是一个绝对平均的结果。表示每个结果都可能出现 n + 1 次。

这是一个较为粗暴的假设。

如果稍微使用一个简单的概率来计算呢?

这里有 2 个概率:

  1. x 变量是否在数组中的概率,有 2 种情况—— 在与不在,所以,他的概率是 1/2.
  2. x 变量出现在数组的概率,有n 种情况,但只会出现一次,所以是 1/n.

我们把两个概率进行相乘,结果是 1/(2n). 这个数,就是“加权值”。

如何使用加权值对上面代码的“复杂度”进行计算?

然后,我们在这公式上把 (n + 1)换成“权重”:也就是 1/2n。

结果为 3n + 1 / 4 。这个也就是“加权后的平均时间复杂度”,表示,执行了 1 + 2 + ···· + n + n 次的“加权平均值”。

如果使用大 O 表示法,去除系数,常数,低阶,那么他的最终结果就是 O(n)。

可以看到,前者使用的是分母没有做任何权重措施,仅仅是简单的 n + 1,而后者,我们做了简单的权重计算,认为出现的概率不是 n + 1,而是 1/2n。

可以说,加权值,是为了在前者的基础上,更加的准确。也就是说,要计算准确的平均时间复杂度,就需要准确的计算这个“权重值”,而权重值会受到数据范围,数据种类影响。因此需要在实际操作中,进行调参。

简单来说,就拿 “x 变量是否在数组中的概率” 这个值来说,不一定是 1/2 ,如果有这样一组数据{y, s, f, f, g, x, g, h}, 那么,他的概率还是 1/2吗,实际上只有 1/8,所以,还是得根据实际情况来。

均摊时间复杂度

个人认为,均摊时间复杂度就是“平均时间复杂度” 的一个特殊版本,相对而言也比较简单。

例如一段代码,在 n -1 种情况下,他的复杂度是 O(1),每 n - 1 次之后,他的时间复杂度是 O(n)。可以看到,这个代码他的最坏时间复杂度实际是固定频率出现的。所以,我们可以把 O(n) 的时间复杂度均摊到其他的 O(1) 复杂度中,得到最终的结果就是 O(1).

对一个数据结构进行一组连续操作中,大部分情况下时间复杂度都很低,只有个别情况下,复杂度很高,而且这些操作存在连贯性和规律性,那么就进行均摊。另外,一般均摊时间复杂度就是最好时间复杂度。

你可能感兴趣的:(平均时间复杂度)