这里使用尾递归有什么好处?

2024-03-17

我一直在阅读描述如何通过使用尾递归版本来降低快速排序的空间复杂度的文章,但我无法理解这是怎么回事。以下是两个版本:

QUICKSORT(A, p, r)
       q = PARTITION(A, p, r)
       QUICKSORT(A, p, q-1)
       QUICKSORT(A, q+1, r)


TAIL-RECURSIVE-QUICKSORT(A, p, r)
   while p < r
      q = PARTITION(A, p, r)
      TAIL-RECURSIVE-QUICKSORT(A, p, q-1)
      p = q+1

(来源 -http://mypathtothe4.blogspot.com/2013/02/lesson-2-variations-on-quicksort-tail.html http://mypathtothe4.blogspot.com/2013/02/lesson-2-variations-on-quicksort-tail.html)

据我了解,这两者都会导致数组的左半部分和右半部分递归调用。在这两种情况下,一次只会处理一半,因此在任何时候只有一个递归调用会使用堆栈空间。我无法看到尾递归快速排序如何节省空间。

上面的伪代码摘自文章 -http://mypathtothe4.blogspot.com/2013/02/lesson-2-variations-on-quicksort-tail.html http://mypathtothe4.blogspot.com/2013/02/lesson-2-variations-on-quicksort-tail.html文章中的解释更让我困惑——

快速排序对给定的子数组进行分区并继续递归两次; 一个在左侧子数组,一个在右侧。这些中的每一个 递归调用将需要其自己的单独的堆栈空间流。 该空间用于存储数组的索引变量 某种程度的递归。如果我们从一开始就想象这种情况发生 到执行结束时,我们可以看到堆栈空间每次都会翻倍 层。

那么尾递归快速排序如何解决所有这些问题呢?

好吧,我们现在只递归两个子数组,而不是递归 一。这样就无需在每一层将堆栈空间加倍 的执行。我们通过使用 while 循环来解决这个问题 执行相同任务的迭代控制。而不是需要 堆栈来保存两个递归调用的变量集,我们只需 更改同一组变量并使用单个递归调用 新变量。

我不明白在常规快速排序的情况下,堆栈空间如何在每一层执行时加倍。

注意:- 本文中没有提及编译器优化。


尾递归函数调用允许编译器执行常规递归通常无法执行的特殊优化。在尾递归函数中,递归调用是最后执行的事情。在这种情况下,编译器不必为每个调用分配堆栈帧,而是可以重新编写代码以简单地重用当前堆栈帧,这意味着尾递归函数将仅使用单个堆栈帧,而不是数百甚至数千。

这种优化是可能的,因为编译器知道一旦进行尾递归调用,就不需要变量的先前副本,因为没有更多的代码要执行。例如,如果递归调用后有打印语句,则编译器需要知道递归调用返回后要打印的变量的值,因此堆栈帧无法重用。

如果您想了解有关“空间节省”和堆栈重用实际如何工作的更多信息,请参阅以下 wiki 页面以及示例:尾调用 http://en.wikipedia.org/wiki/Tail_call

编辑:我没有解释这如何应用于快速排序,是吗?嗯,那篇文章中抛出了一些术语,这让一切都变得混乱(其中一些完全是错误的)。给定的第一个函数 (QUICKSORT) 在左侧进行递归调用,在右侧进行递归调用,然后退出。请注意,右侧的递归调用是函数中发生的最后一件事。如果编译器支持尾递归优化(如上所述),则只有左侧调用创建新的堆栈帧;所有正确的调用都只是重用当前帧。这可以节省some堆栈帧,但仍然会遇到分区创建一系列调用的情况,其中尾递归优化无关紧要。另外,即使右侧调用使用相同的框架,左侧调用也会调用within右侧调用仍然使用堆栈。最坏情况下,堆栈深度为N。

描述的第二个版本是not尾递归快速排序,而是一种仅递归完成左侧排序,而使用循环完成右侧排序的快速排序。事实上,这个快速排序(如另一个用户之前所描述的)不能应用尾递归优化,因为递归调用不是最后执行的事情。这是如何运作的?当正确实现时,对快速排序的第一次调用与原始算法中的左侧调用相同。然而,甚至没有调用右侧递归调用。这是如何运作的?好吧,循环会处理这个问题:它不是“先左后右”排序,而是通过调用对左侧进行排序,然后通过不断地仅对右侧进行排序来对右侧进行排序。左边的右边的。这听起来确实很荒谬,但它基本上只是对如此多的左元素进行排序,使得右元素变成单个元素,不需要排序。这有效地消除了正确的递归,从而减少了函数的递归性(伪递归,如果你愿意的话)。然而,真正的实现并不是每次都只选择左侧;它选择最小的一侧。想法还是一样的;它基本上只在一侧而不是两侧进行递归调用。选择较短的一侧将确保堆栈深度永远不会大于 log2(N),这是正确二叉树的深度。这是因为较短的边始终最多是当前数组部分大小的一半。然而,本文给出的实现并不能确保这一点,因为它可能会遇到“左边是整棵树”的相同最坏情况。如果您愿意阅读更多内容,这篇文章实际上对此给出了很好的解释:基于快速排序的高效选择和部分排序 http://blogs.msdn.com/b/devdev/archive/2006/01/18/514482.aspx

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

这里使用尾递归有什么好处? 的相关文章

  • 快速求解子集和

    考虑这种解决子集和问题的方法 def subset summing to zero activities subsets 0 for activity cost in activities iteritems old subsets sub
  • 使用主方法求解 T(n) = 2T(n/2) + n/log n 和 T(n) = 4T(n/2) + n/log n 之间的差异

    我最近偶然发现了一个资源 其中 2T n 2 n log ntypeMM 宣布复发无法解决 我接受它作为一个引理 直到今天 另一种资源被证明是矛盾的 在某种意义上 根据资源 下面的链接 其中的 Q7 和 Q18 是建议 分别在问题中的1和2
  • 确定解决迷宫问题的最小线段数

    我有一个问题 我需要定义一个具有最少数量的顶点的多边形 该多边形与不透明的图像中的每个像素相交或包含每个像素 令 N 为图像中的像素数 我唯一的假设是图像的边界 孔 内不能包含透明像素 并且至少有两个像素是不透明的 举个例子 假设我有以下图
  • 如何从一组重叠的圆计算多边形集?

    这个问题是一些计算细节的扩展这个问题 https stackoverflow com questions 1667310 combined area of overlapping circles 假设有一组 可能重叠的 圆 并且希望计算这组
  • 高维最近邻搜索的最佳数据结构

    我实际上正在处理高维数据 50 000 100 000 个特征 并且必须对其执行最近邻搜索 我知道随着维度的增长 KD 树的性能很差 而且我还了解到 一般来说 所有空间分区数据结构都倾向于对高维数据执行详尽的搜索 此外 还有两个重要事实需要
  • 固定大小集以包含给定集的最大数量

    我有大约 1000 组尺寸 1 4 1 3 3 5 6 4 5 6 7 5 25 42 67 100 是否有可能找到包含最大数量的给定集合的大小为 20 的集合 检查每一个100 80 20 集 效率低下 我不太确定这是 NP 完全的 考虑
  • 快速搜索压缩文本文件

    我需要能够在大量压缩文件 txt 中搜索文本 压缩可能会改变为其他东西 甚至成为专有的 我想避免解压所有文件并压缩 编码 搜索字符串并在压缩文件中搜索 这应该可以通过对所有文件使用相同的码本使用霍夫曼压缩来实现 我不想重新发明轮子 所以 任
  • 如何将一组重叠范围划分为不重叠范围?

    假设您有一组范围 0 100 一 0 75 b 95 150 c 120 130 d 显然 这些范围在某些点上重叠 您将如何剖析这些范围以生成不重叠范围的列表 同时保留与其原始范围相关的信息 在本例中为范围后面的字母 例如 运行算法后的上述
  • 有人可以解释以下异或属性

    我的一个论坛提到给定的数组n数字 arr 0 n 1 以下条件成立 is the xor运算符 f l r f 0 r f 0 l 1 where f l r arr l arr l 1 arr r 我检查了上面的数组数量和不同的值l an
  • Karasuba算法递归过多

    我正在尝试用 c 实现 Karasuba 乘法算法 但现在我只是想让它在 python 中工作 这是我的代码 def mult x y b m if max x y lt b return x y bm pow b m x0 x bm x1
  • 无需构建树即可预测霍夫曼压缩比

    我有一个二进制文件 我知道其中每个符号出现的次数 如果我要使用霍夫曼算法压缩它 我需要预测压缩文件的长度 我只对假设的输出长度感兴趣 而不对单个符号的代码感兴趣 因此构建霍夫曼树似乎是多余的 作为一个例子 我需要得到类似的东西 包含 4 个
  • URL路径相似度/字符串相似度算法

    我的问题是我需要比较 URL 路径并推断它们是否相似 下面我提供了要处理的示例数据 GROUP 1 robots txt GROUP 2 bot html GROUP 3 phpMyAdmin 2 5 6 rc1 scripts setup
  • 使用多级解决方案计算二维网格中的最近邻

    我有一个问题 在 x y 大小的网格中 我提供了一个点 并且我需要找到最近的邻居 在实践中 我试图在 pygame 中找到距离光标最近的点 该点跨越颜色距离阈值 计算如下 sqrt rgb1 0 rgb2 0 2 rgb1 1 rgb2 1
  • 大数据使用什么数据结构

    我有一个包含一百万行的 Excel 工作表 每行有 100 列 每行代表一个具有 100 个属性的类的实例 列值是这些属性的值 哪种数据结构最适合在这里使用来存储数百万个数据实例 Thanks 这实际上取决于您需要如何访问这些数据以及您想要
  • 两组点之间的最佳匹配

    I ve got two lists of points let s call them L1 P1 x1 y1 Pn xn yn and L2 P 1 x 1 y 1 P n x n y n 我的任务是找到它们点之间的最佳匹配 以最小化它
  • 在java中使用BUBBLE SORT对二维字符串数组进行排序

    类似的问题已经被问过 但从来没有关于二维字符串数组 因此在尝试了很长时间之后我找不到我想要的 我正在尝试使用 BubbleSort 对 java 中的 2D 字符串数组进行排序 作为输入 我收到一个二维字符串数组 一个表 以及您应该排序的
  • 如何用约束标记一大组“传递群”?

    在 NealB解决方案之后进行编辑 与以下解决方案相比 NealB的解决方案非常非常快任何另一个 https stackoverflow com q 18033115 answers and 提出了关于 添加约束以提高性能 的新问题 Nea
  • 每个术语出现的次数

    我得到了一个数组a n 2 where n can be 10 5最大时有n个科目和n个学生 全部编号为 1 2 n a i 0 and a i 1 1 lt i lt n 表示在第 i 个科目中 所有来自a i 0 to a i 1 通过
  • 需要解释搜索最小大和的算法

    我正在解决 Codility 问题作为练习 但无法回答其中一个问题 我在互联网上找到了答案 但我不明白这个算法是如何工作的 有人可以引导我逐步完成它吗 这是问题 You are given integers K M and a non em
  • Prim 的迷宫生成算法:获取相邻单元格

    我基于 Prim 算法编写了一个迷宫生成器程序 该算法是 Prim 算法的随机版本 从充满墙壁的网格开始 选择一个单元格 将其标记为迷宫的一部分 将单元格的墙壁添加到墙壁列表中 While there are walls in the li

随机推荐