我注意到以递归方式调用quicksort的方式存在差异。
一种方法是
quicksort(Array, left, right)
x = partition(Array, left, right)
quicksort(Array, left, x-1)
quicksort(Array, x+1, right)
partition(array, left, right)
pivotIndex := choose-pivot(array, left, right)
pivotValue := array[pivotIndex]
swap array[pivotIndex] and array[right]
storeIndex := left
for i from left to right - 1
if array[i] ≤ pivotValue
swap array[i] and array[storeIndex]
storeIndex := storeIndex + 1
swap array[storeIndex] and array[right] // Move pivot to its final place
return storeIndex
这是有道理的,因为quicksort通过在枢轴周围划分其他元素来工作,因此元素Array [x]应该处于其最终位置。因此,[left,partion-1]和[partition + 1,right]的范围仍然存在。
另一种方式
quicksort(Array, left, right)
x = partition(Array, left, right)
quicksort(Array, left, x)
quicksort(Array, x+1, right)
PARTITION(A,p,r)
x A[p]
i p - 1
j r + 1
while TRUE
do repeat j j - 1
until A[j] x
repeat i i + 1
until A[i] x
if i < j
then exchange A[i] A[j]
else return j
注意缺少-1。这些似乎表明数组已正确分区,但没有一个元素处于最终位置。这两种方式是不可互换的,如果我以第二种方式输入-1,则输入数组被不正确地排序。
造成差异的原因是什么?显然它在分区方法中的某个地方,是否与Hoare或Lumuto的算法有关?
答案 0 :(得分:0)
除了在最小的阵列上运行外,两个版本之间的效率实际上没有太大差异。大部分工作是在分离一个大的 n 大数组时完成的,这些大小的数组可以在多个 n 空间远离它们的正确位置,分成两个较小的数组。即使在最坏的情况下,即使是较小的,也不能有远离其正确位置的价值。单向&#34;单向&#34;本质上在每一步创建三个分区 - 但由于第三个分区只有一个空间大,所以它只对算法的进展做出O(1)贡献。
话虽这么说,实现最终切换非常容易,所以我不确定为什么你的代码以及其他方式&#34;例子没有采取这一步骤。他们甚至指出了一个陷阱(如果为枢轴选择了最后一个而不是第一个元素,递归永远不会结束),这将完全通过实现消除末端的枢轴元素的开关来完全避免。我能想象的唯一情况是使用的优选代码是代码空间绝对溢价。
答案 1 :(得分:0)
如果没有别的,排除或传递分区索引可能是封闭和半开放间隔之间的差异:右边可能是第一个不触摸的索引 - 没有引用的不完整片段没有告诉。
答案 2 :(得分:0)
造成差异是因为partition()
的返回值意味着不同的事情。
One way
中,partition()
的返回值是用于分区的数据透视最终位于Array[x]
之后parition()
是使用的数据透视的位置在partition()
。Other way
中,partition()
的返回值不是用于分区的数据透视最终位于Array[x]
之后的partition()
的元素。少于partition()
中使用的支点,但除此之外我们不太了解。实际的枢轴可以位于阵列上半部分的任何位置。由此可见,x-1
中使用x
而不是Other way
的第一次递归调用可能很容易给出不正确的结果,例如pivot = 8
,Array[x] = 5
和Array[x-1] = 7
。
答案 3 :(得分:-1)
如果您考虑一下,其他方式不会对算法产生任何影响。如果分区算法与第一个中的相同,那么在其中一个子数组中包含枢轴将不会产生任何影响,因为在这种情况下没有其他元素会将其位置与子数组中的枢轴交换。
最多会增加一些数字的比较次数。虽然我不确定它是否会对大型阵列的排序时间产生不利影响。