为什么要在阵列上使用LIFO堆栈?

时间:2016-01-30 22:34:13

标签: arrays performance stack time-complexity

我最近在接受采访时要求我选择两个数据结构来解决问题,现在我有了以下问题:

如果所需的唯一操作是Stackarray,那么在push上使用pop的原因是什么? array为其添加和弹出最后一个元素提供了恒定的时间,与使用Stack实现LinkedList相比,它占用的内存通常更少。如果需要,它还提供随机访问。唯一的推理是因为array通常具有固定大小,所以我们需要为我们放入的每个元素动态调整数组的大小吗?除非惩罚不成比例,否则这仍然是恒定的时间吗?

2 个答案:

答案 0 :(得分:1)

在某些语言(如C ++)中,'堆栈'类使用实际上可以在动态数组(向量),链表(列表)或甚至双端队列(双端队列)之间进行更改。我只提到这个,因为它通常不公平地比较堆栈与数组(一个是接口,另一个是数据结构)。

大多数动态数组实现将分配比所需更多的空间,并且在填充数组时,它们将再次调整为大小的2倍,依此类推。这避免了分配并且保持推送的性能通常是恒定的时间。然而,偶尔的大小调整确实需要复制元素O(n),尽管通常认为这是分摊到恒定时间。所以一般来说,你是正确的,因为这是有效的。

另一方面,链接列表通常需要为每次推送分配,这可能有些昂贵,并且它们创建的节点的大小比数组中的单个元素大。

然而,链表的一个可能的优点是它们不需要连续的内存。如果您有许多元素,则可能无法为数组分配足够大的内存块。话虽如此,链接列表会占用更多内存......所以它有点洗漱。

例如,在C ++中,堆栈默认使用deque容器。 deque通常被实现为一个动态的“页面”阵列。记忆每个内存页面的大小都是固定的,这允许容器实际具有随机访问属性。此外,由于每个页面是分开的,因此整个容器不需要连续的内存,这意味着它可以存储许多元素。对于deque而言,调整大小也很便宜,因为它只是分配了另一个页面,使其成为大型堆栈的绝佳选择。

答案 1 :(得分:1)

这里有几个方面需要考虑......

首先, Stack 是一种抽象数据类型。它没有定义如何实现自己。

数组(通常)是一个定义明确的具体实现,甚至可能是固定大小,除非明确定义为动态。

可以实现动态数组,使其在耗尽时自动增长,并在填充率下降时自动缩小。这些操作不是恒定时间,但实际上摊销到常量时间,因为数组在每次操作中都不会增长或缩小。就内存使用而言,很难想象除非极少使用,否则数组比链表更昂贵。

数组的主要问题是分配大小。这是最大限制和内存碎片的问题。使用链接列表可以避免这两个问题,因为每个条目的内存占用都很小。