摘自第四算法:
1.3.48两层双双出场。用一个双端队列实现两个堆栈,以便每个 操作执行恒定数量的双端队列操作(请参阅练习1.3.33)。
用1个单双端队列实现2个堆栈的含义是什么?有实际原因吗?为什么不直接创建2个堆栈?
1.3.49具有三个堆栈的队列。用三个堆栈实现一个队列,以便每个 队列操作采用恒定数量(最坏情况)的堆栈操作。警告 : 高度困难。
相关问题:How to implement a queue with three stacks?
此外,为什么我必须实现具有三个堆栈的队列?我也不能直接创建队列吗?
答案 0 :(得分:2)
第一个问题看起来更像是设计为练习,而不是其他任何东西。我很怀疑在很多情况下您想使用一个双端队列来实现两个堆栈,尽管我很高兴被证明是错误的。但是,我认为这个问题的目的是让您考虑双端队列和堆栈的“几何形状”。对于非常优雅的问题,有一个非常漂亮的解决方案,如果您看到它是如何工作的,它将使您对所有这些类型的工作方式有更深的了解。
第二个问题-在命令式编程语言中,没有太多理由实现具有三个堆栈的队列。但是,在像Lisp这样的功能编程语言中,堆栈通常很容易实现,但是实际上很难让每个操作需要一定数量的操作的队列工作。实际上,有一段时间,如果我没记错的话,人们相信这根本是不可能的。您可以用两个堆栈实现一个队列(这是一个非常常见的练习,实际上是一个很好的练习,因为生成的队列非常快),但这通常只能提供良好的 admortized 性能,而不是良好的最坏情况的性能,在功能性语言中,摊销不是一件事情,或者很难做到这一点,不一定是个好主意。因此,从三个堆栈中以恒定的复杂性来获取队列是一件大事,因为它释放了使用许多经典算法的能力,这些算法依赖队列,否则在功能上下文中将不可用。
但是,在这两种情况下,它们的主要目的都是作为练习,以帮助您更好地理解基础知识。您实际上会在实践中做这两种事情吗?可能不是-一些图书馆设计师可能会为您做这件事。但是,通过这些练习,您是否可以对这些数据类型如何工作,它们擅长和坏的事情有更深入的了解,以及对库设计师必须如何努力工作的赞赏?是的,完全可以!
答案 1 :(得分:1)
来自双端队列的堆栈
堆栈是先进先出的结构。双端队列使您可以从其前后推动/弹出。如果您跟踪存储在正面/背面的项目数,则可以将正面作为一个堆栈,将背面作为另一个堆栈,如果计数器为零,则返回NULL项。
为什么会这样做?谁知道,但请继续阅读。
来自堆栈的队列
您可以实现一个队列,以使其使用两个堆栈在所有操作上具有 O(1)的摊销时间。将项目放在队列中时,请将它们放在一个堆栈中。当您需要将事物从队列中拉出时,请将该堆栈清空到另一个堆栈中,然后从该堆栈的顶部弹出(同时用新的传入项目填充另一个堆栈)。
为什么要这样做?
因为,这 大致来说就是您如何排队。数据结构必须以某种方式实现。在计算机中,您从基地址开始向外分配内存。因此,堆栈是非常自然的数据结构,因为您要做的就是跟踪单个正偏移量,以了解堆栈顶部在哪里。
直接实施队列更加困难...您正在将项目添加到一端,但又将它们拉离了另一端。两个堆栈为您提供了一种实现方法。
但是为什么要3个队列?
因为此算法(如果存在)可确保对队列操作的时间复杂度有恒定的限制。使用我们的2堆栈解决方案,平均每个项目花费 O(1)时间,但是,如果我们有一个非常大的堆栈,则偶尔会有一次操作需要一个很长的时间发生这种情况时,汽车坠毁,火箭炸毁或患者死亡。
您不希望使用笨拙的算法来提供不可预测的性能。
您要保证。
This StackOverflow answer解释了未知的3层解决方案,但是有6层解决方案。
为什么从双端队列开始堆叠
让我们回到您的第一个问题。如我们所见,有充分的理由能够从堆栈中构建队列。对于计算机,这是从简单的结构构建复杂的数据结构的自然方法。它甚至可以为我们提供性能保证。
Dequeues的堆栈在计算机上并不实用。但是计算机科学与计算机无关。这是关于找到有效的算法解决方案。也许您是在工厂的multi-directional conveyor belt上存储东西。您可以对传送带进行编程,使其像出队一样,并使用出队来堆叠。在这种情况下,这个问题更有意义。
答案 2 :(得分:0)
似乎这些实现没有实际用途。
主要目的是鼓励学生使用简单的工具发明复杂的解决方案-这是每个合格开发人员的重要技能。
(也许次要目标是教程序员实现老板的荒谬愿景:))