为什么C#中的Stack <T>类在ADT中允许ElementAt(index)?

Ash*_*sam 2 c# stack adt data-structures

堆栈是一种抽象数据类型(ADT),应该具有一个密封的操作列表,例如Push(),Pop(),Peek()等,以强制执行后进先出(LIFO)原则。

但是它具有ElementAt(index),允许我访问堆栈中的任何元素。据我了解,Stack应该对不在表面上的元素具有较小的可见性。是不是

Mat*_*son 6

ElementAt()是通过Linq Enumerable.ElementAt()而不是通过Stack<T>接口提供的。

之所以可行,是因为Stack<T>实现IEnumerable<T>是实现所需要的全部,ElementAt()因为它所做的就是迭代通过提供的所有元素,IEnumerable<T>直到访问了N个元素为止。

对于a,Stack<T>这是O(N)操作。如果ElementAt()与a一起使用,List<T>则内部优化会将其转换为O(1)运算。

至于为什么要使用Stack<T>工具IEnumerable<T>-只有一位设计师可以真正回答这个问题。由于它是一个非变异操作,因此它实际上并没有违反堆栈的任何基本要求。我它是为了方便而提供的。

正如/ u / Damien_The_Unbeliever在他的答案中指出的那样,代码可以通过将N个元素弹出到另一个堆栈,然后以相反的顺序将它们全部推回,从而确定没有IEnumerable接口的第N个元素,从而使原始堆栈保持不变。

美中不足的是,Microsoft 没有记录 Stack的IEnumerable返回元素顺序。您可以检查源代码以查看它确实按LIFO顺序返回了元素-但这根本没有记录。

对此问题的答案中对此进行了讨论

无论如何,要为您所讨论的抽象堆栈定义ADT接口?我认为没有确切的答案。严格来说,您可以说一个堆栈只有Push()and Pop()。但是大多数实现也提供了Count

正如有关Wikipedia上Stack的文章所述

在许多实现中,堆栈具有比“推”和“弹出”更多的操作。一个示例是“堆栈顶部”或“窥视”,它观察最顶部的元素而不将其从堆栈中移除。

由于可以使用具有相同数据的“弹出”和“推”完成操作,因此这不是必需的。如果堆栈为空,则在“堆栈顶部”操作中可能会发生下溢情况,与“弹出”相同。同样,实现通常具有一个仅返回堆栈是否为空的函数。

因此,从根本上讲,您的问题的答案是:

库设计人员决定除了仅具有mutation方法Push()和之外,还添加一些非mutation便捷方法Pop()

  • 然后OP可能会问为什么堆栈会实现“ IEnumerable”,因为它也不是理论堆栈结构所隐含的契约的一部分。 (2认同)