我正在使用以下链接的堆栈阅读快速排序实现。
我的问题是关于以下段落。
将较大的小子文件放在堆栈上的策略 确保堆栈中的每个条目不超过一半 它下面的一个大小,所以堆栈需要包含空间 只有大约lg N个条目。当最大堆栈使用时发生 分区始终位于文件的中心。对于随机文件, 实际最大堆栈大小要低得多;对于退化文件吧 可能很小。
这种技术不一定适用于真正的递归 实现,因为它取决于终止或尾递归删除。 如果一个过程的最后一个动作是调用另一个过程,一些 编程环境会安排诸如局部变量之类的东西 在呼叫之前而不是之后从堆栈中清除。 如果没有结束递归删除,我们无法保证堆栈大小 对于quicksort来说会很小。
作者的意思是“堆栈中的每个条目不超过其下一个条目的一半”?你能举个例子吗。
作者如何得出结论,堆栈只需要约lg N
个条目的空间?
authore的意思是“如果没有移除末尾递归,我们无法保证快速排序的堆栈大小会很小”?
感谢您的时间和帮助。
答案 0 :(得分:1)
将较大的小子文件放在堆栈上的策略可确保堆栈中的每个条目不超过其下一个大小的一半,
这不太正确。考虑您要对100个元素的数组进行排序,第一个数据库正好位于中间。然后你有一个堆栈
49
50
然后从堆栈中弹出49个元素的部分,分区,然后将两个部分推到堆栈上。让我们说这次枢轴的选择不是很好,有20个元素不大于枢轴。然后你就得到了堆栈
20
28
50
并且每个堆栈条目超过下一个堆栈条目的一半。
但这不能永远持续下去,而且我们有
在整个排序过程中,如果堆栈级别
k
被占用,则其大小最多为total_size / (2^k)
。
当排序开始时,这显然是正确的,因为在堆栈上只有一个元素,在0级,这是整个大小为total_size
的数组。
现在,假设所述属性在进入循环(while(!stack.empty())
)时保持不变。
从堆栈级s
弹出长度为m
的子数组。如果s <= 1
,则在下一次循环迭代之前没有其他任何操作,并且不变量继续保持。否则,如果s >= 2
,在对其进行分区之后,有两个新的子阵列要在堆栈上推送,其中s-1
个元素在一起。这两个中较小的一个的大小为smaller_size <= (s-1)/2
,较大的大小为larger_size <= s-1
。堆栈级m
将被两者中的较大者占用,我们有
larger_size <= s-1 < s <= total_size / (2^m)
smaller_size <= (s-1)/2 < s/2 <= total_size / (2^(m+1))
表示堆栈级别m
。 m+1
在循环体的末尾。不变量适用于下一次迭代。
由于最多只有一个大小为0的子数组在堆栈中(然后在下一次迭代中立即弹出),所以堆栈级别永远不会超过lg total_size + 1
。
关于
作者的意思是“如果没有移除末尾递归,我们无法保证快速排序的堆栈大小会很小”?
在递归实现中,您可以进行深度递归,并且当堆栈帧不被重用于结束调用时,您可能需要线性堆栈空间。考虑一个愚蠢的透视选择,总是选择第一个元素作为枢轴,并选择已经排序的数组。
[0,1,2,3,4]
分区,枢轴进入位置0,较小的子阵列为空。对较大子数组[1,2,3,4]
的递归调用,分配一个新的堆栈帧(所以现在有两个堆栈帧)。同样的原则,下一个递归调用子数组[2,3,4]
分配第三个堆栈帧等。
如果有一个去除了末尾递归,即重复使用堆栈帧,则会有与上面手动堆栈相同的保证。
答案 1 :(得分:0)
我会尽力回答你的问题(希望我没错)...... 快速排序的每一步都将您的输入分为两个(一半)。通过这样做,您需要logN。这解释了你的第一个和第二个问题(“堆栈中的每个条目不超过一半”和“logN”条目)