在最坏的情况下,Quicksort递归深度需要O(n)的堆栈空间。为什么在最坏的情况下它不会导致大型集合的堆栈溢出? (逆序)
答案 0 :(得分:6)
如果你在枢轴的两侧递归,那么在最坏的情况下它会导致堆栈溢出以获得足够大的数据。这就是为什么没有人在生产代码中使用天真的QuickSort。
您可以对算法进行简单的更改,以防止Omega(n)
最坏情况下的堆栈使用。在每个分区之后,递归地Quicksort“小半部分”,然后迭代地循环以执行“大半部分”。这需要O(log n)
额外的堆栈空间。如果您愿意,可以通过再次修改它来使O(1)
堆栈空间和O(log n)
额外的非堆栈空间。将“大半部分”推到预先分配的数组(或您选择的其他后进先出数据结构)的末尾,循环执行“小半部分”,当您点击底部弹出最后一个关闭数据结构的元素。
您可以进行进一步的更改以避免Omega(n^2)
最坏情况运行时。但是,它不再是一个天真的QuickSort,它是一个具有中位数的中位数 - 枢轴选择的QuickSort,或者是一个Introsort,或者其他什么。
答案 1 :(得分:2)
那么为什么在最坏的情况下它不会导致大量有序数据的堆栈溢出
确实如此。为什么你会认为它没有?
(但请注意,快速排序的最坏情况输入取决于您选择的枢轴。通常不是相反的顺序 - 事实上,对于一个天真的枢轴选择,另一个最坏情况输入是已经排序的序列,它不会不得不逆转。)
但是现在排序算法的库实现很少是快速排序,正是出于这个原因。例如,C ++ std::sort
使用introsort代替,这是一个经过修改的快速排序,只要它过深地处理就会切换到不同的排序算法。
答案 2 :(得分:1)
在反向序列(最糟糕的情况)的情况下,它可能导致堆栈溢出。因为数组对于内部函数堆栈来说可能太大。 (例如,99,000,000个元素)您可以使用stack
数据结构替换递归。您将循环while (stck.empty() == false)
function quicksort(arr[0,1,...n-1])
stack.push(pair(0, n - 1))
while stack not empty
interval = stack.pop()
...
...
stack.push(interval(0, pivot - 1))
stack.push(interval(pivot + 1, n - 1))