理解时间复杂度 O(log n)

解决实际问题时,代码是否高效会成为问题解决的关键。高效的算法可以将资源开销控制在有限范围内将问题解决。而衡量一个算法优劣的标准则是时间复杂度,我们经常会看到 O(n) 这样的记号。

对于 O(1) ,它表示时间复杂度为1,即解决这一问题的实现很直接,只需一步或者固定几个步骤。比如从哈希表中取值,通过索引只需在哈希表中查找一次便得到结果。而 O(n) 表示完成这一任务的时间复杂度与输入有关,与输入成正比,比如遍历数组。

而 O(log n) 又该怎样理解?它的典型场景便是二分查找法,原理是不断将输入进行二等分直到找到目标。

假设我们要从如下一个已经排好序长度为16的数列中找到指定的数字13:

一个长度为16已经排好序的数列

一个长度为16已经排好序的数列

  • 找到中间元素,这里也就是元素16所处的位置,它中间为分界线将整个数列均分。

选中正中间的元素作为分界线

选中正中间的元素作为分界线

  • 将目标与中间元素进行比较,因为13小于16,所以我们保留前半部分继续寻找。

保留前半部分

保留前半部分

  • 重复上面的步骤,找到中间元素 8,因为13比8大,所以保留8后面的这部分

重复上面的步骤

重复上面的步骤

  • 继续上面的步骤,直到没有剩余的元素便找到我们的目标了。

直到找到目标

直到找到目标

上面的步骤,每一次操作都将总数减小到原来的一半。我们从16个元素中找一个目标时二分了4次,16*(1/2)*(1/2)*(1/2)*(1/2) 总结成公式:

16个元素中二分的公式

16个元素中二分的公式

推而广之,从 n 个元素中查找,需要多少次呢,还不知道,假设需要 k次。那么用上面的公式则可以表示为:

从 n 个元素中查找的公式

从 n 个元素中查找的公式

去掉幂的括号则变成了:

去掉公式中幂的括号

去掉公式中幂的括号

最后我们去掉分母,得到了一个简洁的公式:

简洁的公式

简洁的公式

此时如何求 k 的值呢。就需要用到对数了。

如果你对对数感到陌生了,可以先来回顾一下它的定义,请看来自百科的解释:

在数学中,对数是对求幂的逆运算,正如除法是乘法的倒数,反之亦然。 这意味着一个数字的对数是必须产生另一个固定数字(基数)的指数。 -- 百度百科 - 对数

所以对上面公式两边取对数便得到了 k 的值,

对数公式

对数公式

于是,我们推导出了如果输入为 n,查找一个结果的时间复杂度为 O(log n)。

相关资源