是否有人能够提供一个'简单的英语'直观但正式的解释QuickSort n log n的原因?根据我的理解,它必须通过n个项目,并且它会记录n次...我不知道如何将其记入单词中,为什么它会记录n次。
答案 0 :(得分:107)
log n
部分来自这样一个事实:它(至少理想情况下)在每次迭代时将输入分成两半。从N个项目开始,每次打破这些项目意味着在log 2 (N)次迭代之后你已经达到了1个项目,此时你显然无法进一步细分它。例如,从128个项目开始,您可以分为64个,32个,16个,8个,4个,2个,1个项目组 - 7个迭代(并且log 2 (128)= 7 )。
这些迭代中的每一个都会扫描整个数组以对其进行分区,因此每个迭代都具有O(N)复杂度。
在两者之间,你最终得到O(log N)操作,每个操作都有O(n)复杂度,O(n log n)总体复杂度。
从技术上讲,Quicksort的Big-O实际上是O(N 2 )。这是因为分区元素的选择足够糟糕,可能会将阵列分成一侧的一个元素,另一侧的阵列的其余部分。如果在每次迭代时都发生这种情况,则需要N次迭代将其分解为每个元素的一个元素,因此您可以获得N个操作,每个操作的复杂度为O(N),总体上为O(N * N)。
在一个真正的实现中,你通常会在此之前停止,但这是你能走得最远的。
答案 1 :(得分:42)
每个分区操作都需要O(n)次操作(数组一次传递)。 平均而言,每个分区将数组划分为两个部分(总计记录n个操作)。总共我们有O(n * log n)操作。
即。在平均log n分区操作中,每个分区都需要进行O(n)操作。
答案 2 :(得分:2)
嗯,它并不总是n(log n)。选择的枢轴大约在中间时的性能时间。在最坏的情况下,如果您选择最小或最大元素作为枢轴,则时间将为O(n ^ 2)。
要显示'n log n',您可以假设pivot是最接近要排序的数组中所有元素的平均值的元素。 这会将阵列分成大致相同长度的2个部分。 在这两个方面,您都应用快速排序程序。
在每个步骤中,你继续将数组的长度减半,你将在log n(基数2)时间内执行此操作,直到达到length = 1,即1个元素的排序数组。
答案 3 :(得分:1)
分两部分打破排序算法。首先是分区,第二是递归调用。分割的复杂度为O(N),理想情况下递归调用的复杂度为O(logN)。例如,如果您有4个输入,则将有2(log4)个递归调用。两者相乘得到O(NlogN)。这是一个非常基本的解释。
答案 4 :(得分:0)
实际上你需要找到所有N个元素(枢轴)的位置,但是每个元素的最大比较数是logN(第一个是N,第二个是N / 2,3rd N / 4 ..假设枢轴是中位数元素)
答案 5 :(得分:0)
对数背后有一个直觉:
在达到1之前可以将数字n除以常数的次数为O(log n)。
换句话说,如果您看到一个带有O(log n)项的运行时,则很有可能会发现一些重复缩小了恒定因子的东西。
在快速排序中,缩小的常数是每个级别上最大的递归调用的大小。 Quicksort的工作原理是:选取一个枢轴,将数组分成两个小于该枢轴的元素和大于枢轴的元素的子数组,然后递归地对每个子数组进行排序。
如果您随机选择枢轴,则所选枢轴将有50%的机会位于元素的中间50%,这意味着两个子数组中的较大者最多有80%的机会原尺寸的75%。 (你知道为什么吗?)
因此,对于为什么快速排序在时间O(n log n)上运行的一个很好的直觉如下:递归树中的每一层都起作用O(n),并且由于每个递归调用都有减小大小的机会至少要有25%的空间,我们希望在元素用尽之前要有O(log n)层,以扔出阵列。
当然,这假设您是随机选择枢轴。快速排序的许多实现都使用试探法来尝试找到不需要太多工作的枢轴,而不幸的是,这些实现可能在最坏的情况下导致整体运行时很差。 @Jerry Coffin对这个问题的绝妙答案是关于快速排序的一些变体,这些变体通过切换使用的排序算法来保证O(n log n)的最坏情况,这是寻找有关此信息的好地方。