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