问题 为什么我们“使用2个堆栈实现队列”? [重复]


可能重复:
为什么要使用两个堆栈来建立队列? 

我得到了这个赋值问题,要求我使用两个堆栈实现一个队列。我的问题不是如何做到,而是为什么这样做?我不是来自计算机背景,我试图找到答案,但无法真正找到原因吗?我认为你的专家可以帮助我理解实现这样的事情有什么好处。我找到了一篇相关的文章 为什么要使用两个堆栈来建立队列? 谈论这个,但想知道是否还有更多。


6923
2017-09-12 23:59


起源

我认为没有任何优势。他们只是想看看你是否能够很好地理解这两种数据结构 做 它。好吧,也许有些语言有内置堆栈数据类型,但不是队列。 - Tom Zych
@Tom:真的很快。这确实有帮助。万分感谢。 - smandape
@Oli:我经历过它并且无法从中获得真正的答案。所以我又问了一遍。感谢您的帮助。 - smandape
@smandape:如果您无法理解其他问题的答案,那么您真正的问题可能是关于纯功能数据结构的目的吗? - hugomg
不幸的是,得到一个答案不会让问题变成一个骗局,所以我 上午 投票结束。 - paxdiablo


答案:


这样做有几个很好的理由。

首先,一些函数式编程语言(如Haskell,ML或Lisp)支持列表作为内置类型。列表通常表示为单个前向链接列表,这意味着它们支持O(1)前置但O(n)连接。在这样的语言中,制作一个堆栈非常容易 - 你只需要一个列表就可以将第一个元素推送到pop中。由于内部实现,这在O(1)时间内运行。如果您尝试使用此类列表创建队列,则enqueue将花费O(n)时间,因为您必须添加到单个链接列表的末尾,该列表不存储指向最后一个元素的指针。另一方面,如果使用两个堆栈(或更多堆栈)实现队列 胡德 - 梅尔维尔队列 使用六个!),然后即使你只有你的语言堆栈,你也可以分摊O(1)入队和出队。尽管已经设计了更高级的数据结构来支持纯功能队列和列表(例如 2-3指树),双堆栈结构在许多应用中仍然非常有用。

除此之外,在某些情况下,您可能希望使用特殊堆栈来实现队列以获得额外的功能。例如,你可以 增加堆栈数据结构 支持O(1)find-min / find-max。如果你有这样的堆栈,那么你可以 使用双栈结构来创建一个也具有O(1)find-min / find-max的队列。试图直接解决这个问题要困难得多(看看我的 更复杂的建筑 使用这些属性创建队列!)

最后,从理论角度来看,知道可以使用两个堆栈模拟队列是很有趣的。在可计算性理论中,a 双栈下推自动机 是一种理论计算设备,功率相当于图灵机。一个 队列自动机 是一个类似的结构,它使用队列而不是两个堆栈。因为我们知道您可以模拟具有两个堆栈的队列,所以您可以立即证明队列自动机至少与图灵机一样强大,因此队列自动机是图灵完备的。

希望这可以帮助!


15
2017-09-13 00:04



这真的有很大的帮助,让像我这样的非计算机人清楚地理解事物。感谢您的帮助,我感谢答案中提供的链接。 - smandape
只是出于兴趣,你有一个断言,断言实现一个队列作为两个堆栈是摊销O(1)? - paxdiablo
@ paxdiablo-这里有很棒的资源(cs.cmu.edu/afs/cs.cmu.edu/academic/class/15750-s04/www/...这是由展开堆的发明者Daniel Sleator。希望这可以帮助! - templatetypedef
@hattenn它不一定是不可变的(你也可以用这种方式创建一个可变队列),但它是实现不可变队列的好方法。采用这种方法很容易,没有推送和弹出操作改变队列,但返回一个新队列。所以你现在保持旧队列不变,换一个新队列。一个有趣的特性是,由于这些队列是不可变的,并且内部堆栈也可以是不可变的,那么具有不同堆栈的这些不同队列将包含许多共同的子堆栈(因为它们是不可变的)可以安全地是相同的堆栈,内存使用率低。 - Jon Hanna
@templatetypedef:我认为队列自动机参数的方向是错误的:假设我有一个图灵机,你必须构建一个模仿它的队列自动机。你将我的图灵机转换成双栈自动机,然后......什么?将队列自动机转换为双栈自动机?但是哪个队列自动机?您需要显示的内容是基于队列的两个堆栈模拟;您可以用您的假设显示的是队列自动机不是 更多 比图灵完成。 - Jonas Kölker