为什么快速排序不能用尾递归来实现[原创]

本文为辟谣文。

百度上大为流传的以下代码

 

博客一:

http://blog.csdn.net/zhiren2011/article/details/47004841

这其实并不是尾递归,相比最普通的快速排序,也没有任何优化效果。

 

真相在这里:

http://stackoverflow.com/questions/9247504/how-to-implement-tail-recursive-quick-sort-in-scala#comment11651078_9247504

本问题的高赞答案和高赞评论揭示了为什么快速排序无法使用尾递归来降低其空间复杂度。

 

首先,要解释的是尾递归的意义:

尾递归是用来优化递归算法空间复杂度的,其原理是当递归调用出现在函数的最后一步时,编译器就可以丢弃当前函数的调用帧,这样,整个递归过程中,就仅仅存在一个调用帧,这样就减小了内存的消耗。

这句话并非其看起来如此简单,

从原理来讲,尾递归优化的关键在于丢弃当前函数的调用帧内存,这意味着该调用帧必须是可被丢弃的,

而关键在于,仅当函数在其最后一步调用自身的时候,其当前调用帧才可以被丢弃,否则必须保留调用帧以便后续运算的访问。

对于上述博客一当中的所谓“尾递归快速排序”,在进入第一次递归调用时,由于后续还要进行

start=index+1;

以及下一个循环的运算,因此当前帧是无法被丢弃的,最终的调用帧的栈深度为最后一次左侧排序的调用深度。

而分析普通的快速排序的话,可以发现其调用帧堆栈结构和上述所谓“尾递归快速排序”是完全一样的。

因为快速排序是先执行最小(或最大)且最深一级分块的排序,然后执行其对应点另一侧分块,然后返回到父级分块执行父级分块的另一侧分块的排序的。

在整个过程中,任何父级分块信息,也就是运算当中的调用帧数据都必须被保存。就像上述stackoverflow中高赞评论所说的:

You can't -- the idea of the algorithm is to break the work in smaller tasks, perform them recursively and then to put the results together. Because the latter is nessecarily the last thibg you do in quicksort, it can't be tail recursive.

无论从算法原理的角度,还是从满足“仅在最后一步调用自身”的优化前提,

都可以知道,快速排序是不可能允许丢弃当前调用帧的,除非你用一个堆栈来保存这些调用帧的数据,如同上述stackoverflow中高赞答案的解释:

Of course, you need either a really big structure or a really small stack to have a practical problem with a non-tail-recursive divide and conquer algorithm

由于调用帧不仅保存你所需要的分块信息,也会保存其他一些特定语言或编译器所需要的信息,诸如上下文信息等,因此使用额外的堆栈来保存这些数据确实会使内存使用有一定的(线性的)降低,但就像给一个复杂度乘以一个常数一样,这并不能改变快速排序的空间复杂度,只是按一定比例降低了其内存消耗。并且这种方式的效率可能要比编译器自动生成调用帧的效率低得多。

 

根据上述stackoverflow问题中的高赞评论和高赞答案,我们可以得出一个结论:

任何通过把大问题分成小问题一一解决,再将其结果一一拼接起来的算法,都是不可能实现尾递归的。因为解决每个小问题的时候,必须保存得到此小问题的过程中的所有分块信息(调用帧)。

你可以把所有递归算法写成循环算法,但是任何无法实现尾递归的算法,将其写成循环之后,也必然需要一个与其递归版本调用帧堆栈结构相同的堆栈数据结构来保存其调用帧数据。

转载于:https://www.cnblogs.com/Totooria-Hyperion/p/5799862.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值