Algorithm 设计数据结构就像改进的堆栈

Algorithm 设计数据结构就像改进的堆栈,algorithm,data-structures,stack,runtime,time-complexity,Algorithm,Data Structures,Stack,Runtime,Time Complexity,我被要求设计一个数据结构,它将像一个堆栈一样工作,不受大小限制,在给定的运行时限制下,它将支持以下方法 push(s)-将s推送到数据结构-O(1) pop()-删除并返回插入的最后一个元素O(1) middle()-按插入顺序返回索引为n/2的元素(不删除),其中n是数据结构中当前元素的数量。-O(1) peekAt(k)-按插入顺序返回第k个元素(堆栈底部为k=1)O(log(k)) 我曾想过使用链表,并始终保持一个指向中间元素的指针,但后来我在实现peekAt(k)时遇到了问题。有什么想法

我被要求设计一个数据结构,它将像一个堆栈一样工作,不受大小限制,在给定的运行时限制下,它将支持以下方法

push(s)-将s推送到数据结构-O(1)
pop()-删除并返回插入的最后一个元素O(1)
middle()-按插入顺序返回索引为n/2的元素(不删除),其中n是数据结构中当前元素的数量。-O(1)
peekAt(k)-按插入顺序返回第k个元素(堆栈底部为k=1)O(log(k))


我曾想过使用链表,并始终保持一个指向中间元素的指针,但后来我在实现peekAt(k)时遇到了问题。有什么想法可以实现吗?

使用双链表实现对我来说很有意义
Push
Pop
将像通常对堆栈那样实现;对“中间”元素的访问将通过一个附加引用完成,该引用将在
Push
Pop
上更新,具体取决于所包含元素的数量是从偶数变为奇数,还是从奇数变为偶数。
peekAt
操作可以使用二进制搜索来完成。

如果O(1)限制可以放宽到摊销O(1),典型的可变长度数组实现就可以了。当您为当前长度为N的数组分配空间时,请在末尾预留N个额外空间。一旦超出此边界,按照相同的策略重新分配新的大小,将旧内容复制到那里并释放旧内存。当然,您必须维护堆栈的分配长度和实际长度。操作
middle
peek
可以在O(1)中轻松完成

相反,如果阵列占用的空间少于分配空间的1/4(如果需要),也可以缩小阵列


所有业务将按O(1)摊销。其确切含义是,对于自启动以来的任何K个堆栈操作,您总共必须执行O(K)条指令。特别是,N次推送后的重新分配数量将为O(log(N)),并且由于重新分配而复制的元素总量将不超过1+2+4+8…+N在典型的链表中,二进制搜索无法跳转到O(1)中的任意索引。指向中心元素的指针将在
Push
Pop
上更新。中心,是的。但是当你从段say[0,1000]到[500,1000]时,你如何在O(1)中找到第750个元素?@Gassa谢谢你的评论,我犯了一个错误。@Codor“我想使用链表”?或者,与其重新分配整个阵列,不如维护一个堆栈阵列。也就是说,您分配一个大小为X的堆栈。当该堆栈填满时,您分配另一个大小为X的堆栈,并向数组中添加另一项。当有人要求输入项目i时,您可以将i除以X(块大小),索引到引用数组中,然后定位块。然后,一个简单的模运算将为您提供块中的偏移量。增长时仍在调整数组的大小,但只移动了几个指针,而且很少移动。比复制整个堆栈内容要好。@JimMischel:在这里,您仍然需要一个动态增长的数据结构,这次是为了维护指向堆栈的指针数组。本质上,您只是将这个动态结构的大小除以X,代价是在每个操作上取消引用一两个指针。