我目前正在学习Big O Notation运行时间和摊销时间。我理解 O(n)线性时间的概念,意味着输入的大小会成比例地影响算法的增长...例如,二次时间 O(n 2 )等。甚至算法,例如置换生成器, O(n!)次,通过阶乘增长。
例如,以下函数是 O(n),因为算法与其输入 n 成比例增长:
f(int n) {
int i;
for (i = 0; i < n; ++i)
printf("%d", i);
}
同样,如果有嵌套循环,则时间为O(n 2 )。
但究竟是什么 O(log n)?例如,说完整二叉树的高度是 O(log n)是什么意思?
我知道(可能不是非常详细)Logarithm是什么,在某种意义上:log 10 100 = 2,但我无法理解如何识别具有对数时间的函数。 / p>
答案 0 :(得分:2428)
我无法理解如何识别具有日志时间的函数。
对数运行时函数最常见的属性是:
或
这就是为什么,例如,在电话簿中查找人是O(log n)。您无需检查电话簿中的每个人员以找到合适的人员;相反,您可以通过根据字母顺序查看其名称来简单地分而治之,并且在您最终找到某人的电话号码之前,您只需要在每个部分中探索每个部分的子集。
当然,更大的电话簿仍会花费更长的时间,但它的增长速度不会随着额外规模的成比例增长而增长。
我们可以扩展电话簿示例,以比较其他类型的操作和他们的运行时间。我们将假设我们的电话簿具有商店(“黄页”),其具有唯一的名称,而人(“白页”)可能没有唯一的名称。电话号码最多分配给一个人或企业。我们还假设需要一段时间才能翻到特定页面。
以下是我们可能在电话簿上执行的一些操作的运行时间,从最佳到最差:
O(1)(最佳情况):根据商家名称所在的网页和商家名称,找到电话号码。
O(1)(平均情况):根据个人姓名所在的页面及其姓名,找到电话号码。
O(log n):根据一个人的姓名,通过在您尚未搜索的书的部分中间选择一个随机点来查找电话号码,然后检查看看这个人的名字是否在那一点上。然后在书的一部分中间重复这个过程。 (这是对个人姓名的二元搜索。)
O(n):查找电话号码包含数字“5”的所有人。
O(n):根据电话号码,找到具有该号码的个人或公司。
O(n log n):打印机的办公室出现了混乱,我们的电话簿的所有页面都是以随机顺序插入的。通过查看每个页面上的第一个名称然后将该页面放在新的空白电话簿中的适当位置来修复排序以使其正确。
对于以下示例,我们现在在打印机办公室。电话簿等待邮寄给每个居民或企业,每个电话簿上都有一个标签,标明应该邮寄到哪里。每个人或企业都会收到一本电话簿。
O(n log n):我们想要个性化电话簿,因此我们将在指定的副本中找到每个人或公司的名称,然后在这本书并为他们的赞助写了一篇简短的感谢信。
O(n 2 ):办公室发生错误,每个电话簿中的每个条目都有一个额外的“0”电话号码结束。取一些白色并删除每个零点。
O(n·n!):我们已准备好将电话簿加载到装运码头。不幸的是,应该装载书籍的机器人已经乱了!它正在以随机的顺序把书放到卡车上!更糟糕的是,它将所有书籍加载到卡车上,然后检查它们是否按正确顺序排列,如果没有,则将其卸载并重新开始。 (这是可怕的 bogo sort 。)
O(n n ):您修复了机器人,使其正确加载。第二天,你的一个同事对你进行恶作剧,并将装卸码头机器人连接到自动打印系统。每次机器人装载原始书籍时,工厂打印机都会重复运行所有电话簿!幸运的是,机器人的错误检测系统非常复杂,以至于当机器人遇到重复的书籍进行加载时,机器人不会尝试打印更多的副本,但它仍然需要加载已打印的每本原始和重复的书籍。
有关更多数学解释,您可以在此处查看时间复杂度如何到达log n
。 https://hackernoon.com/what-does-the-time-complexity-o-log-n-actually-mean-45f94bb5bfbf
答案 1 :(得分:542)
这个问题已经发布了许多好的答案,但我相信我们确实错过了一个重要的答案 - 即图示的答案。
说完整二叉树的高度是O(log n)是什么意思?
下图描绘了二叉树。注意每个级别如何包含与上面的级别相比节点数量的两倍(因此二进制):
二进制搜索是复杂度O(log n)
的示例。假设图1中树的底层中的节点表示某些已排序集合中的项。二进制搜索是一种分而治之的算法,该图显示了我们将需要(最多)4次比较来查找我们在这16项数据集中搜索的记录。
假设我们有一个包含32个元素的数据集。继续上面的绘图,发现我们现在需要进行5次比较才能找到我们要搜索的内容,因为当我们乘以数据量时,树只增长了一层。因此,算法的复杂性可以描述为对数阶。
在一张普通纸上绘制log(n)
,将产生一个图形,其中曲线的上升随着n
的增加而减速:
答案 2 :(得分:519)
O(log N)
基本上意味着当n
呈指数上升时,时间会线性上升。因此,如果计算1
元素需要10
秒,则计算2
元素需100
秒,计算3
1000
秒元素,等等。
当我们划分和征服算法类型(例如二进制搜索)时,O(log n)
。另一个例子是快速排序,每次我们将数组分成两部分,每次需要O(N)
时间来找到一个数据元素。因此它N O(log N)
答案 3 :(得分:222)
下面的解释是使用完全平衡二叉树的情况来帮助您了解我们如何获得对数时间复杂度。
二叉树是这样一种情况,其中大小为n的问题被分成大小为n / 2的子问题,直到我们遇到大小为1的问题:
这就是你得到的O(log n),这是在上面的树上需要完成的工作量才能达到解决方案。
具有O(log n)时间复杂度的常见算法是二进制搜索,其递归关系为T(n / 2)+ O(1),即在树的每个后续级别将问题划分为一半并且执行恒定量额外的工作。
答案 4 :(得分:152)
<强>概述强>
其他人已经提供了很好的图表示例,例如树形图。我没有看到任何简单的代码示例。因此,除了我的解释之外,我还提供了一些带有简单打印语句的算法来说明不同算法类别的复杂性。
首先,您想要了解Logarithm,您可以从https://en.wikipedia.org/wiki/Logarithm获得。自然科学使用e
和自然日志。工程学徒将使用log_10(日志库10),计算机科学家将使用log_2(日志库2),因为计算机是基于二进制的。有时,您会将自然日志的缩写视为ln()
,工程师通常会将_10关闭,只使用log()
,而log_2缩写为lg()
。所有类型的对数都以类似的方式增长,这就是为什么它们共享同一类log(n)
。
当您查看下面的代码示例时,我建议查看O(1),然后是O(n),然后是O(n ^ 2)。在你善待这些之后,再看看其他人。我已经包含了干净的示例以及变体来演示微妙的更改如何仍然可以导致相同的分类。
您可以将O(1),O(n),O(logn)等视为增长的类别或类别。某些类别比其他类别需要更多时间。这些类别有助于为我们提供一种排序算法性能的方法。随着输入n的增长,一些增长得更快。下表以数字方式说明了所述增长。在下表中,将log(n)视为log_2的上限。
各种大O类别的简单代码示例:
O(1) - 常数时间示例:
算法1打印一次hello并且它不依赖于n,因此它将始终以恒定时间运行,因此它是O(1)
。
print "hello";
算法2打印hello 3次,但它不依赖于输入大小。即使n增长,该算法也将始终只打印3次hello。这就是说3,是一个常数,所以这个算法也是O(1)
。
print "hello";
print "hello";
print "hello";
O(log(n)) - 对数示例:
算法3演示了一个在log_2(n)中运行的算法。注意for循环的post操作将i的当前值乘以2,因此i
从1到2到4到8到16到32 ......
for(int i = 1; i <= n; i = i * 2)
print "hello";
算法4演示了log_3。注意i
从1到3到9到27 ......
for(int i = 1; i <= n; i = i * 3)
print "hello";
算法5很重要,因为它有助于显示只要数字大于1并且结果重复地与自身相乘,那么您正在查看对数算法。
for(double i = 1; i < n; i = i * 1.02)
print "hello";
O(n) - 线性时间示例:
这个算法很简单,打印你好n次。
for(int i = 0; i < n; i++)
print "hello";
此算法显示一个变体,它将打印hello n / 2次。 n / 2 = 1/2 * n。我们忽略1/2常数并看到该算法是O(n)。
for(int i = 0; i < n; i = i + 2)
print "hello";
O(n * log(n)) - nlog(n)示例:
将此视为O(log(n))
和O(n)
的组合。 for循环的嵌套有助于我们获得O(n*log(n))
for(int i = 0; i < n; i++)
for(int j = 1; j < n; j = j * 2)
print "hello";
算法9与算法8类似,但每个循环都允许变化,这仍然导致最终结果为O(n*log(n))
for(int i = 0; i < n; i = i + 2)
for(int j = 1; j < n; j = j * 3)
print "hello";
O(n ^ 2) - n平方示例:
O(n^2)
。
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
print "hello";
与算法10类似,但有一些变化。
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j = j + 2)
print "hello";
O(n ^ 3) - n立方体示例:
这类似于算法10,但有3个循环而不是2个。
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
for(int k = 0; k < n; k++)
print "hello";
与算法12类似,但有些变体仍会产生O(n^3)
。
for(int i = 0; i < n; i++)
for(int j = 0; j < n + 5; j = j + 2)
for(int k = 0; k < n; k = k + 3)
print "hello";
<强>摘要强>
上面给出了几个直接的例子和变体,以帮助证明可以引入哪些微妙的变化,而这些变化实际上并没有改变分析。希望它能给你足够的洞察力。
答案 5 :(得分:126)
如果你有一个功能:
1 millisecond to complete if you have 2 elements.
2 milliseconds to complete if you have 4 elements.
3 milliseconds to complete if you have 8 elements.
4 milliseconds to complete if you have 16 elements.
...
n milliseconds to complete if you have 2**n elements.
然后它需要log 2 (n)时间。松散地说,Big O notation意味着只有大n的关系才需要,而且常数因子和较小的项可以忽略不计。
答案 6 :(得分:91)
对数运行时间(O(log n)
)实质上意味着运行时间与输入大小的对数成比例增长 - 例如,如果10个项目最多占用一定数量的时间x
,最多100个项目,例如2x
,最多10,000个项目4x
,那么它看起来像O(log n)
时间复杂度。
答案 7 :(得分:73)
对数
好的,让我们尝试并完全理解对数实际是什么。
想象一下,我们有一根绳子,我们把它绑在一匹马上。如果绳子直接绑在马上,那么马需要拉开的力量(例如,从一个男人身上)直接是1.
现在想象一下绳子环绕着一根杆子。逃跑的马现在必须更加努力。时间量取决于绳索的粗糙度和杆的大小,但我们假设它会将力量乘以10(当绳索完全转弯时)。
现在如果绳子一圈,那么马需要拉10次。如果人类决定让这匹马真的很难,他可能会再次将绳子绕在一根杆子上,使其强度增加10倍。第三个循环将再次将强度增加10倍。
我们可以看到,对于每个循环,该值增加10.获得任何数字所需的回合数称为数字的对数,即我们需要3个帖子将您的力量倍增1000倍,6个帖子要倍增你的实力是1,000,000。
3是1,000的对数,6是1,000,000(基数10)的对数。
那么O(log n)究竟意味着什么?
在上面的示例中,我们的增长率为&#39; O(log n)。对于每一个额外的循环,我们的绳索可以处理的力是10倍:
Turns | Max Force
0 | 1
1 | 10
2 | 100
3 | 1000
4 | 10000
n | 10^n
现在上面的例子确实使用了基数10,但幸运的是,当我们谈论大写符号时,日志的基数是微不足道的。
现在让我们想象你想要一个1-100之间的数字。
Your Friend: Guess my number between 1-100!
Your Guess: 50
Your Friend: Lower!
Your Guess: 25
Your Friend: Lower!
Your Guess: 13
Your Friend: Higher!
Your Guess: 19
Your Friend: Higher!
Your Friend: 22
Your Guess: Lower!
Your Guess: 20
Your Friend: Higher!
Your Guess: 21
Your Friend: YOU GOT IT!
现在你需要7个猜测才能做到这一点。但这里的关系是什么?你可以从每个额外的猜测中猜出最多的项目是什么?
Guesses | Items
1 | 2
2 | 4
3 | 8
4 | 16
5 | 32
6 | 64
7 | 128
10 | 1024
使用图表,我们可以看到,如果我们使用二进制搜索来猜测1-100之间的数字,那么我们最多 7次尝试。如果我们有128个数字,我们也可以猜测7个尝试中的数字,但是129个数字将使我们最多 8个尝试(与对数的关系,这里我们需要7个猜测128个值范围, 1024个值范围的10个猜测.7是128的对数,10是1024的对数(基数2)。
请注意,我最多加注了#39;。大写符号总是指更糟糕的情况。如果你很幸运,你可以在一次尝试中猜出这个数字,所以最好的情况是O(1),但这是另一个故事。
我们可以看到,每次猜测我们的数据集都在缩小。确定算法是否具有对数时间的一个好的经验法则是 在每次迭代后查看数据集是否按特定顺序缩小
O(n log n)怎么样?
你最终会遇到一个线性时间 O(n log(n)算法。上面的经验法则再次适用,但这次对数函数必须运行n次,例如减小尺寸列表 n次,出现在像mergesort这样的算法中。
您可以轻松识别算法时间是否为n log n。寻找一个遍历列表(O(n))的外部循环。然后看看是否有内循环。如果内循环切割/减少每次迭代的数据集,则该循环为(O(log n),因此整体算法= O(n log n)强>
免责声明:绳索对数示例来自优秀的Mathematician's Delight book by W.Sawyer。
答案 8 :(得分:56)
您可以通过说时间与N中的位数成比例来直观地考虑O(log N)。
如果操作对输入的每个数字或位执行恒定时间工作,则整个操作将花费与输入中的位数或位数成比例的时间,而不是输入的幅度;因此,O(log N)而不是O(N)。
如果一个操作做出一系列恒定时间决定,其中每一个都要减半(减少因子3,4,5 ......)要考虑的输入大小,整体将花费时间与log base 2成比例(基数3,基数4,基数5 ......)输入的大小N,而不是O(N)。
等等。
答案 9 :(得分:51)
我总是必须在心理上可视化在O(log n)中运行的算法的最佳方法如下:
如果将问题大小增加一个乘法量(即将其大小乘以10),则工作量仅增加一个附加量。
将此应用于您的二叉树问题,以便您有一个好的应用程序:如果您将二叉树中的节点数加倍,则高度仅增加1(附加量)。如果你再次加倍,它仍然只增加1.(显然我假设它保持平衡等)。这样,当问题大小成倍增加时,你的工作量不会增加一倍,而是只做更多的工作。这就是O(log n)算法很棒的原因。
答案 10 :(得分:41)
什么是log b (n)?
在达到大小为1的部分之前,您可以将长度为n的对数重复切换为b等份的次数。
答案 11 :(得分:39)
首先,我建议你阅读以下书籍;
这是一些功能及其预期的复杂性。数字表示语句执行频率。
Big-O复杂性图表也取自bigocheatsheet
最后非常简单的展示展示了如何计算;
解剖程序的语句执行频率。
分析程序的运行时间(示例)。
答案 12 :(得分:18)
划分和征服算法通常具有logn
组件的运行时间。这来自重复减半的输入。
在二进制搜索的情况下,每次迭代都会丢弃一半的输入。应该注意的是,在Big-O表示法中,log是log base 2。
编辑:如上所述,日志库无关紧要,但在推导算法的Big-O性能时,日志因子将来自减半,因此我将其视为基数2。
答案 13 :(得分:15)
但究竟什么是O(log n)?例如,说&gt;完整二叉树的高度是O(log n)是什么意思?
我将其改为'完整二叉树的高度为log n'。如果您逐步向下遍历,则确定完整二叉树的高度将为O(log n)。
我无法理解如何识别具有对数的函数 时间。
对数基本上是取幂的倒数。因此,如果函数的每个“步骤”都消除了原始项集中的因子元素,那么这就是对数时间算法。
对于树示例,您可以轻松地看到,当您继续遍历时,降低节点级别会减少指数数量的元素。查看名称排序电话簿的流行示例基本上等同于遍历二进制搜索树(中间页面是根元素,您可以在每个步骤推断出是向左还是向右)。
答案 14 :(得分:12)
这两种情况需要O(log n)时间
case 1: f(int n) {
int i;
for (i = 1; i < n; i=i*2)
printf("%d", i);
}
case 2 : f(int n) {
int i;
for (i = n; i>=1 ; i=i/2)
printf("%d", i);
}
答案 15 :(得分:10)
O(log n)有点误导,更准确地说是O(log 2 n),即(以2为基数的对数)。
平衡二叉树的高度为O(log 2 n),因为每个节点都有两个(注意“2”,如log 2 n)节点。因此,具有n个节点的树的高度为log 2 n。
另一个例子是二元搜索,它的运行时间为O(log 2 n),因为在每一步你将搜索空间除以2。
答案 16 :(得分:10)
O(log n)
是指在与对数成比例的时间内工作的函数(或算法或算法中的步骤)(在大多数情况下通常为2,但并非总是如此,无论如何这是输入大小的大O符号*)无关紧要。
对数函数是指数函数的反函数。换句话说,如果您的输入呈指数级增长(而非线性增长,正如您通常所认为的那样),则您的函数会线性增长。
O(log n)
运行时间在任何一种分而治之的应用程序中都很常见,因为你(理想情况下)每次都将工作量削减了一半。如果在每个分工或征服步骤中,你正在做恒定的时间工作(或工作不是恒定时间,但随着时间的增长比O(log n)
慢),那么你的整个函数是O(log n)
。每个步骤在输入上需要线性时间是相当普遍的;这相当于O(n log n)
的总时间复杂度。
二进制搜索的运行时复杂性是O(log n)
的一个示例。这是因为在二进制搜索中,通过将数组分成两半并且仅关注每一步的一半,在每个后续步骤中总是忽略输入的一半。每个步骤都是常量时间,因为在二进制搜索中,您只需要将一个元素与您的键进行比较,以便在不考虑您在任何时候考虑的数组大小的情况下确定下一步该做什么。所以你做了大约log(n)/ log(2)步骤。
合并排序的运行时复杂性是O(n log n)
的一个示例。这是因为您将每个步骤将数组分成两半,从而导致总共大约log(n)/ log(2)步骤。但是,在每个步骤中,您需要对所有元素执行合并操作(无论是对n / 2个元素的两个子列表进行合并操作,还是对n / 4个元素的四个子列表进行两次合并操作,都是无关紧要的,因为它增加了必须为每个步骤中的n个元素执行此操作)。因此,总复杂度为O(n log n)
。
*请记住,大O符号,by definition,常量无关紧要。同样由change of base rule表示对数,不同基数的对数之间的唯一差异是一个常数因子。
答案 17 :(得分:9)
它只是意味着此任务所需的时间随log(n)增长(例如:n = 10时为2s,n = 100时为4s,......)。阅读Binary Search Algorithm和Big O Notation上的维基百科文章以获得更多精确度。
答案 18 :(得分:9)
简单地说:在算法的每一步,您都可以将工作量减半。 (渐近相当于第三,第四,......)
答案 19 :(得分:8)
如果你在图形计算器或类似的东西上绘制对数函数,你会发现它上升得非常慢 - 甚至比线性函数慢。
这就是为什么具有对数时间复杂度的算法备受追捧的原因:即使对于非常大的n(例如,n = 10 ^ 8),它们的表现也超过了可接受的程度。
答案 20 :(得分:7)
我可以添加一些有趣的东西,很久以前我在Kormen等书中读过。现在,想象一个问题,我们必须在问题空间中找到解决方案。这个问题空间应该是有限的。
现在,如果你可以证明,在你的算法的每次迭代中,你切断了这个空间的一小部分,这不小于某个限制,这意味着你的算法在O(logN)时间运行。
我应该指出,我们在这里谈的是相对分数限制,而不是绝对限制。二分搜索是一个经典的例子。在每一步,我们都会抛弃1/2的问题空间。但二元搜索并不是唯一的例子。假设,你以某种方式证明,在每一步你扔掉至少1/128的问题空间。这意味着,您的程序仍在O(logN)时间运行,但速度明显慢于二进制搜索。这是分析递归算法的一个非常好的暗示。通常可以证明,在每一步中递归都不会使用多个变量,这会导致问题空间中某些部分的截止。
答案 21 :(得分:7)
但究竟什么是O(log n)
这恰恰意味着“当n
趋于infinity
时,time
倾向于a*log(n)
,其中a
是一个恒定的缩放因子。” / p>
实际上,它并不意味着;更有可能意味着“time
除以a*log(n)
趋向于1
”。
“倾向于”具有“分析”中通常的数学意义:例如,“如果你选择任何任意任意小的非零常数k
,那么我可以找到一个对应的值X
,((time/(a*log(n))) - 1)
小于k
,n
大于X
的所有值。“
用非专业术语来说,这意味着时间等式可能还有其他一些组成部分:它可能有一些不断的启动时间;但是这些其他成分对于n的大值是微不足道的,而a * log(n)是大n的主要术语。
请注意,如果等式是,例如......
time(n)= a + b log(n)+ c n + d n n
...那么这将是O(n平方),因为无论常数a,b,c和非零d的值是什么,d*n*n
项总是优于其他项对于任何足够大的n值。
这就是O符号的意思:它意味着“任何足够大的n的主导项的顺序是什么”。
答案 22 :(得分:6)
我可以给一个for循环的例子,也许一旦掌握了这个概念,也许在不同的语境中理解它会更简单。
这意味着在循环中,步骤呈指数增长。例如。
for (i=1; i<=n; i=i*2) {;}
该程序的O符号的复杂性为O(log(n))。让我们尝试手动循环(n介于512和1023之间(不包括1024):
step: 1 2 3 4 5 6 7 8 9 10
i: 1 2 4 8 16 32 64 128 256 512
虽然n介于512和1023之间,但只发生了10次迭代。这是因为循环中的步骤呈指数增长,因此只需10次迭代即可达到终止。
x的对数(到a的基数)是^ x的反函数。
就像说对数是指数的倒数。
现在试着这样看,如果指数增长得非常快,那么对数增长(反向)非常慢。
O(n)和O(log(n))之间的差异很大,类似于O(n)和O(a ^ n)之间的差异(a是常数)。
答案 23 :(得分:5)
log x to base b = y
是b^y = x
如果您有深度为d且大小为n的M-ary树,则:
遍历整棵树~O(M ^ d)= O(n)
在树中走一条路径~O(d)= O(log n到基数M)
答案 24 :(得分:5)
在信息技术方面,它意味着:
f(n)=O(g(n)) If there is suitable constant C and N0 independent on N,
such that
for all N>N0 "C*g(n) > f(n) > 0" is true.
Ant似乎这个符号主要来自数学。
在本文中有一个引用: D.E. Knuth, "BIG OMICRON AND BIG OMEGA AND BIG THETA", 1976:
根据这里讨论的问题,我建议成员 SIGACT,计算机科学和数学期刊的编辑, 采用上面定义的符号,除非更好的选择 很快就找到了。
今天是2016年,但我们今天仍然使用它。
在数学分析中,它意味着:
lim (f(n)/g(n))=Constant; where n goes to +infinity
但即使在数学分析中,有时这个符号也用于“C * g(n)&gt; f(n)&gt; 0”的意思。
正如我从大学所知,这个符号是由德国数学家兰道(1877-1938)引入的。
答案 25 :(得分:5)
实际上,如果你有一个n个元素的列表,并从该列表中创建一个二叉树(比如分而治之算法),你将继续除以2,直到达到大小为1的列表(叶子)。
在第一步,你除以2.你有2个列表(2 ^ 1),你将每个除以2,所以你有4个列表(2 ^ 2),再划分,你有8个列表( 2 ^ 3)依此类推,直到你的列表大小为1
这给你的等式:
n/(2^steps)=1 <=> n=2^steps <=> lg(n)=steps
(你拿每边的lg,lg是原木底座2)
答案 26 :(得分:5)
每次我们编写算法或代码时,我们都会尝试分析它的渐近复杂性。 它与时间复杂度不同。
渐近复杂度是算法执行时间的行为,而时间复杂度是实际执行时间。但是有些人可以互换使用这些术语。
因为时间复杂性取决于各种参数,即
1.物理系统
2.编程语言
3.编码风格
还有更多......
实际执行时间不是分析的好方法。
相反,我们将输入大小作为参数,因为无论代码是什么,输入都是相同的。 因此执行时间是输入大小的函数。
以下是线性时间算法
的示例
线性搜索
给定n个输入元素,要搜索数组中的元素,您需要最多'n'个比较。换句话说,无论您使用何种编程语言,您喜欢哪种编码风格,执行它的系统。在最坏的情况下,它只需要n次比较。执行时间与输入大小成线性比例。
它不仅仅是搜索,无论是工作(增量,比较还是任何操作),它都是输入大小的函数。
所以当你说任何算法都是O(log n) 它表示执行时间是日志乘以输入大小n。
随着输入大小的增加,完成的工作(这里是执行时间)增加。(因此比例)
n Work
2 1 units of work
4 2 units of work
8 3 units of work
当输入大小增加时,所做的工作量增加,并且它独立于任何机器。 如果你试图找出工作单位的价值 它实际上依赖于那些上面指定的参数。它将根据系统和所有参数而改变。
答案 27 :(得分:3)
如果您正在寻找基于直觉的答案,我想为您提出两种解释。
想象一座非常高的山丘,底部也很宽阔。到达山顶有两种方式:一种是围绕山顶螺旋形地穿过顶部的专用通道,另一种是用于雕刻的小露台,以提供楼梯。现在,如果第一种方式达到线性时间O(n),则第二种方式是O(log n)。
想象一个算法,它接受一个整数n
作为输入,并按照与n
成比例的时间完成,然后它是O(n)或theta(n)但是如果它在时间与number of digits or the number of bits in the binary representation on number
成比例,然后算法在O(log n)或theta(log n)时间内运行。
答案 28 :(得分:2)
分而治之范式中的算法具有复杂度O(logn)。这里有一个例子,计算你自己的幂函数,
int power(int x, unsigned int y)
{
int temp;
if( y == 0)
return 1;
temp = power(x, y/2);
if (y%2 == 0)
return temp*temp;
else
return x*temp*temp;
}
来自http://www.geeksforgeeks.org/write-a-c-program-to-calculate-powxn/
答案 29 :(得分:2)
完整的二进制示例是O(ln n),因为搜索如下所示:
1 2 3 4 5 6 7 8 9 10 11 12
搜索4个产生3次点击:6次,3次然后4次。然后log2 12 = 3次,这对于需要多少次点击是一个很好的选择。
答案 30 :(得分:0)
我想补充一点,树的高度是从根到叶子的最长路径的长度,并且节点的高度是从该节点到叶子的最长路径的长度。路径表示在两个节点之间遍历树时遇到的节点数。为了实现O(log n)时间复杂度,树应该是平衡的,这意味着任何节点的子节点之间的高度差应该小于或等于1.因此,树并不总是保证时间复杂度O(log n),除非它们是平衡的。实际上在某些情况下,在最坏的情况下,在树中搜索的时间复杂度可能是O(n)。
您可以查看余额树,例如AVL tree
。这个用于在插入数据时平衡树,以便在树中搜索时保持(log n)的时间复杂度。
答案 31 :(得分:0)
O(logn)是多项式时间复杂度之一,用于衡量任何代码的运行时性能。
希望您已经听说过二进制搜索算法。
假设您必须在大小为N的数组中找到一个元素。
基本上,代码执行就像 ñ N / 2 N / 4 N / 8 ....等
如果对每个级别完成的所有工作求和,将得到n(1 + 1/2 + 1/4 ....),等于O(logn)
答案 32 :(得分:0)
简单地说:
循环的下一次迭代所花费的时间是当前循环所花时间的两倍-> n ^ 2
循环的下一次迭代与当前循环的时间相同-> n
循环的下一次迭代将花费当前循环的一半时间-> log 2 (n)
循环的下一次迭代需要当前循环的1/3倍-> log 3 (n)
循环的下一次迭代需要当前循环的1/4倍-> log 4 (n)
当涉及渐近分析时,我们只需要调用log(n)即可,它基本上可以是任何底数,如上所示。但是由于计算机科学家比其他类型的树使用更多的二叉树,因此在大多数情况下,我们将log 2 (n)称为log(n)。