我读过qsort
是一种通用排序算法,没有任何关于实现的承诺。我不知道各个平台库之间的差异,但是假设Mac OS X和Linux的实现大致相似,qsort
的实现是递归和/或需要大量堆栈吗?
我有一个大数组(成千上万个元素),我想在不使堆栈崩溃的情况下对其进行排序。或者,是否有类似的针对大数组的排序算法可以推荐?
你知道,递归部分的深度是logn。在64个递归级别(大约是64*4=256字节的堆栈总大小)中,您可以对大小约为2^64的数组排序,即您可以在64位CPU上寻址的最大数组大小147573952589676412928字节,您甚至无法将其全部存储在内存中!
我认为应该关注有意义的事情。
是的,它是递归的。不过,它可能不会使用大量的堆栈。为什么不试一下呢?递归并不是某种可怕的东西 - 它是许多问题的首选解决方案。
qsort
不需要超过log2(N)层递归(即栈深度),其中N是给定平台上最大数组大小。请注意,这个限制适用于无论分区好坏如何,即它是递归的最差情况深度。例如,在32位平台上,递归深度在最坏情况下永远不会超过32,假设qsort
实现合理。qsort
实际上可以是另一种排序方法,比如归并排序、插入排序,甚至冒泡排序:Pqsort
的实现可能不是递归的。qsort
和bsearch
的复杂性。幸运的是,这个问题特别涉及到两个具体的实现,所以标准基本上是无关紧要的。如果苹果在下一个版本中倒行逆施地将OS X切换到Bogosort,那么他们是否能够得逞并不取决于它是否违反了C标准... - Steve Jessop使用快速排序,堆栈将以对数方式增长。您需要非常多的元素才能使堆栈溢出。
我猜现代大多数实现的qsort
实际上使用了Introsort算法。一个合理编写的快速排序不会导致栈溢出(它会先对较小的分区进行排序,从而将栈深度限制在对数增长范围内)。
然而,Introsort更进一步——为了限制最坏情况下的复杂度,如果它发现快速排序效果不好(递归太多,可能导致O(N2)的复杂度),它会切换到堆排序,保证O(N log2 N)的复杂度,并且也限制了栈的使用。因此,即使它所使用的快速排序写得不够好,切换到堆排序仍然会限制栈的使用。
qsort
实现是极其糟糕的。如果你真的很担心,我建议你去看看源代码,但我怀疑任何半靠谱的实现都会使用原地排序算法或者使用malloc
来分配临时空间,并在malloc
失败时退回到原地排序算法。一个朴素的快速排序实现(仍然是qsort的流行选项)的最坏空间复杂度为O(N)。 如果修改实现以首先对较小的数组进行排序并且使用尾递归优化或显式堆栈和迭代,那么最坏情况下的空间可以降至O(log N),(大多数答案已经写了)。因此,如果快速排序的实现没有问题,并且库没有被不当的编译器标志破坏,则不会使堆栈溢出。但是,例如,大多数支持尾递归消除的编译器不会在未优化的调试构建中执行此优化。使用错误标志构建的库(例如,在嵌入式领域中,有时需要构建自己的调试libc)可能会导致堆栈崩溃。
对于大多数开发人员来说,这永远不会成为问题(他们拥有供应商测试过的具有O(log N)空间复杂度的libc),但我认为定期关注潜在的库问题是个好主意。
更新:这里是我的一个例子:2000年libc中的一个错误,其中qsort将开始扰乱虚拟内存,因为qsort实现会在内部切换到mergesort,因为它认为有足够的内存来容纳临时数组。
http://sources.redhat.com/ml/libc-alpha/2000-03/msg00139.html