Algorithm 快速排序的尾部调用优化
我刚刚接触到TCO,我理解了它如何重用单个堆栈帧的概念,而不是每次方法调用自己时创建一个新的堆栈帧。我直观地想将此结构与Algorithm 快速排序的尾部调用优化,algorithm,sorting,Algorithm,Sorting,我刚刚接触到TCO,我理解了它如何重用单个堆栈帧的概念,而不是每次方法调用自己时创建一个新的堆栈帧。我直观地想将此结构与while循环进行比较,因为循环将持续对一组变量执行操作,直到不满足条件为止 然而,鉴于TCO只使用一个堆栈帧,似乎不可能执行诸如快速排序之类的排序算法,一旦递归方法“展开”并在达到基本情况后备份调用堆栈,就需要使用TCO作为堆栈帧的引用。如果不参考调用该方法的次数,它如何知道要执行哪个后续操作以及要执行多少次 如果每次调用的方法体都是相同的,我想它可以只保留一个对方法被调用次
while
循环进行比较,因为循环将持续对一组变量执行操作,直到不满足条件为止
然而,鉴于TCO只使用一个堆栈帧,似乎不可能执行诸如快速排序之类的排序算法,一旦递归方法“展开”并在达到基本情况后备份调用堆栈,就需要使用TCO作为堆栈帧的引用。如果不参考调用该方法的次数,它如何知道要执行哪个后续操作以及要执行多少次
如果每次调用的方法体都是相同的,我想它可以只保留一个对方法被调用次数的引用,然后在方法体中保留一个指针,指示从何处开始执行,但这只是一个猜测
感谢您的帮助。TCO可以在递归调用处于最终位置时执行。快速排序需要两个递归调用,因此它们显然不能同时处于该位置——但其中一个可以,这样尾部调用可以转换为
而循环:
原件(伪代码):
快速排序(i,j){
返回当递归调用位于最终位置时是否可以执行jTCO。快速排序需要两个递归调用,因此它们显然不能都位于该位置,但其中一个可以,这样尾部调用可以转换为,而循环:
原件(伪代码):
快速排序(i,j){
return if j您能更深入地了解一下您所说的“最终位置”吗?在TCO示例中,您在对quicksort的递归调用之后设置了i
。什么使调用成为最终调用?@louism2:i
在消除第二次递归调用的版本中设置,将循环限制设置为(现在已消除)最后一次递归调用。(向上回答正确,向下回答注释过滤器。)递归函数调用的顺序很重要。如果确保尾部调用用于较大的分区,则可以保证最大堆栈深度不超过log2(n)
即使分区选择不当。@louism2:i=k+1
赋值替换了一个最终的递归调用,这是一个重要条件。当您(或编译器)执行TCO时,您必须设置参数,以便在while循环的顶部看起来就像调用了函数一样。在这种情况下,第二个参数不会更改,但如果更改了(例如,对于其他可实现TCO的递归算法),您也需要对j进行赋值。您能更深入地了解一下您的意思吗“最终位置?”在TCO示例中,您在对quicksort的递归调用后设置了i
。什么使调用成为最终调用?@louism2:i
在消除第二次递归调用的版本中设置,将循环限制设置为(现在已消除)最终递归调用的参数。(向上回答正确,向下回答注释过滤器。)递归函数调用的顺序很重要。如果您确保尾部调用用于较大的分区,则可以保证最大堆栈深度不超过log2(n)
即使分区选择不当。@louism2:i=k+1
赋值替换了一个最终的递归调用,这是一个重要条件。当您(或编译器)执行TCO时,您必须设置参数,以便在while循环的顶部,它看起来就像调用了函数一样。在这种情况下,第二个参数不会更改,但如果更改了(例如,对于其他可实现TCO的递归算法),您也需要为j赋值。
quicksort(i, j) {
return if j <= i
k = getPivot(i, j)
partition(i, j, k)
quicksort(i, k-1) <--- This recursive call can't be changed
quicksort(k+1, j) <--- This recursive call is amenable to TCO
}
quicksort(i, j) {
while (j > i) {
k = getPivot(i, j)
partition(i, j, k)
quicksort(i, k-1) <--- This recursive call is unchanged
i = k+1
}
}