Design a max stack data structure that supports the stack operations and supports finding the stack's maximum element.
Implement the MaxStack
class:
MaxStack()
Initializes the stack object.void push(int x)
Pushes elementx
onto the stack.int pop()
Removes the element on top of the stack and returns it.int top()
Gets the element on the top of the stack without removing it.int peekMax()
Retrieves the maximum element in the stack without removing it.int popMax()
Retrieves the maximum element in the stack and removes it. If there is more than one maximum element, only remove the top-most one.
Example 1:
Input ["MaxStack", "push", "push", "push", "top", "popMax", "top", "peekMax", "pop", "top"] [[], [5], [1], [5], [], [], [], [], [], []] Output [null, null, null, null, 5, 5, 1, 5, 1, 5] Explanation MaxStack stk = new MaxStack(); stk.push(5); // [5] the top of the stack and the maximum number is 5. stk.push(1); // [5, 1] the top of the stack is 1, but the maximum is 5. stk.push(5); // [5, 1, 5] the top of the stack is 5, which is also the maximum, because it is the top most one. stk.top(); // return 5, [5, 1, 5] the stack did not change. stk.popMax(); // return 5, [5, 1] the stack is changed now, and the top is different from the max. stk.top(); // return 1, [5, 1] the stack did not change. stk.peekMax(); // return 5, [5, 1] the stack did not change. stk.pop(); // return 1, [5] the top of the stack and the max element is now 5. stk.top(); // return 5, [5] the stack did not change.
Constraints:
-107 <= x <= 107
- At most
104
calls will be made topush
,pop
,top
,peekMax
, andpopMax
. - There will be at least one element in the stack when
pop
,top
,peekMax
, orpopMax
is called.
Follow up: Could you come up with a solution that supports O(1)
for each top
call and O(logn)
for each other call?
题目要求设计一个堆栈除了支持一般堆栈具有的接口函数:push(), pop(), top(),还需要支持返回堆栈中的最大值:peekMax(),和弹出堆栈中的最大值:popMax()。
本题可以在系统自带的堆栈的基础上实现题目所要求的堆栈。如果不用系统自带的而是让我自己实现一个堆栈的话,通常我们会用双向链表来实现,因为双向链表的插入和删除操作的时间复杂度可以达到在O(1)。
如果是一个普通的堆栈那么用一个双向链表就足够了,因为只要在链表头做插入和删除操作就足够了。但是还要支持peekMax()和popMax()的话,那就要求我们能够迅速找到链表中具有最大值的那个节点,遍历链表来寻找的时间复杂度是O(n)无法满足题目要求,因此可以借助于一个有序的字典SortedDict来实现快速查找,用SortedDict来存储键-值(key-values)的最大特点就是字典里的所有键是有序的,详细使用可以参考用Python刷LeetCode必备知识点2 - SortedDict。
对于本题,用SortedDict存储的键(keys)-值(values)分别是节点值和节点指针。由于keys是有序的那就可以快速获得最大值也就是键列表中的最后那一个SortedDict.keys()[-1],于是就可以得到具有最大值的节点指针,如果是删除操作就可以在链表中把对应的节点删除。SortedDict的插入操作时间复杂度是O(logn),满足题目要求。
from sortedcontainers import SortedDict
class ListNode:
def __init__(self, val = None, prev = None, next = None):
self.val = val
self.next = next
self.prev = prev
class MaxStack:
def __init__(self):
self.head = ListNode(-1)
self.tail = ListNode(-1)
self.head.next = self.tail
self.tail.prev = self.head
self.v2n = SortedDict()
def push(self, x: int) -> None:
node = ListNode(x, self.head, self.head.next)
self.head.next.prev = node
self.head.next = node
if x not in self.v2n:
self.v2n[x] = [node]
else:
self.v2n[x].append(node)
def pop(self) -> int:
node = self.head.next
self.head.next = node.next
node.next.prev = self.head
self.v2n[node.val].pop()
if not self.v2n[node.val]:
self.v2n.pop(node.val)
return node.val
def top(self) -> int:
return self.head.next.val
def peekMax(self) -> int:
val= self.v2n.keys()[-1]
return val
def popMax(self) -> int:
val = self.v2n.keys()[-1]
node = self.v2n[val].pop()
if not self.v2n[val]:
self.v2n.pop(val)
node.next.prev = node.prev
node.prev.next = node.next
return node.val