我正在学习大O符号的运行时间和摊销时间。我理解O(n)线性时间的概念,这意味着输入的大小成比例地影响算法的增长。。。例如,二次时间O(n2)等也是如此。甚至是通过阶乘增长的算法,如置换生成器,其O(n!)次。

例如,以下函数为O(n),因为算法与其输入n成比例增长:

f(int n) {
  int i;
  for (i = 0; i < n; ++i)
    printf("%d", i);
}

类似地,如果存在嵌套循环,时间将为O(n2)。

但O(log n)到底是什么?例如,说一个完整的二叉树的高度是O(log n)是什么意思?

我确实知道(也许不是很详细)对数是什么,从这个意义上说:log10 100=2,但我不知道如何识别具有对数时间的函数。


当前回答

如果你正在寻找一个基于直觉的答案,我想为你提供两种解释。

想象一下一座很高的山,它的底部也很宽。要到达山顶,有两种方式:一种是一条围绕山顶螺旋延伸的专用通道,另一种是切割出的小露台状雕刻,以提供楼梯。现在,如果第一种方式在线性时间O(n)内到达,则第二种方式是O(logn)。想象一个算法,它接受整数n作为输入,并在时间上与n成比例地完成,那么它是O(n)或θ。

其他回答

对数运行时间(O(log n))本质上意味着运行时间与输入大小的对数成比例增长-例如,如果10个项目最多需要一定的时间x,100个项目最多花费2倍,10000个项目最多耗费4倍,那么它看起来像是O(log n)时间复杂性。

O(logn)有点误导,更准确地说,它是O(log2n),即(以2为底的对数)。

平衡二叉树的高度是O(log2n),因为每个节点都有两个(注意log2n中的“两个”)子节点。因此,具有n个节点的树的高度为log2n。

另一个例子是二进制搜索,它的运行时间为O(log2n),因为在每一步中,您都将搜索空间除以2。

O(logn)指的是一个函数(或算法,或算法中的步骤),其工作时间与输入大小的对数成正比(大多数情况下通常以2为基数,但并不总是以2为底,在任何情况下,通过big-O符号*,这都是无关紧要的)。

对数函数是指数函数的倒数。换句话说,如果您的输入呈指数增长(而不是通常认为的线性增长),则函数呈线性增长。

O(logn)运行时间在任何一种分而治之的应用程序中都很常见,因为(理想情况下)每次都会将工作减半。如果在每一个除法或征服步骤中,你都在做恒定时间的工作(或不是恒定时间的,但随着时间的增长比O(log n)慢),那么你的整个函数就是O(log)。相当常见的是,每个步骤都需要输入线性时间;这将相当于O(n log n)的总时间复杂度。

二进制搜索的运行时间复杂性是O(logn)的一个例子。这是因为在二进制搜索中,通过将数组分成两半,并且每一步只关注一半,您总是忽略后面每一步的一半输入。每一步都是恒定的时间,因为在二进制搜索中,您只需要将一个元素与关键字进行比较,就可以确定下一步要做什么,而不管您考虑的数组在任何时候都有多大。因此,大约执行log(n)/log(2)步。

合并排序的运行时间复杂性是O(n log n)的一个例子。这是因为每一步都将阵列一分为二,总共约为log(n)/log(2)步。然而,在每一步中,您都需要对所有元素执行合并操作(无论是对n/2个元素的两个子列表执行一次合并操作,还是对n/4个元素的四个子列表执行两次合并操作都是无关紧要的,因为这增加了每一步对n个元素执行合并的必要性)。因此,总复杂度为O(n log n)。

*记住,根据定义,big-O表示法并不重要。同样,通过改变对数的基数规则,不同基数的对数之间的唯一差异是一个常数因子。

这仅仅意味着该任务所需的时间随着log(n)的增加而增加(例如:n=10时为2s,n=100时为4s,…)。请阅读维基百科关于二进制搜索算法和大O符号的文章以了解更多的精度。

简单地说:在算法的每一步,你都可以将工作减半。(渐近等价于第三、第四、…)