Max Stack solution and explanation
9 more videos on this problem
Implement A Max Stack - A Stack With A .max() API (Similar To "Min Stack" on LeetCode)Back To Back SWE · 16:37 · 24K views
LeetCode 716 | Max Stackleetuition · 17:20 · 12K views
Mock Coding Interview: Pivot Index and Max Stack (Python)abrar · 51:47 · 10K views
Max Stack | Leetcode 716Pepcoding · 30:45 · 5K views
Max Stack | LeetCode 716 (Hard)Destination FAANG · 12:38 · 4K views
Max Stack Optimal approach | O(log(n)) | Leetcode 716Pepcoding · 51:57 · 4K views
Leetcode 716. Max Stack | Python Solution | LinkedIn Interview QuestionDonald Dang · 7:50 · 3K views
Leetcode 716(easy). Max Stack【Stack/Design】中文AndroidBabies安卓大宝贝们 · 19:06 · 1K views
Max Stack - LeetCode 716 - JavaScott Hacks Code · 15:21 · 682 views
Leetcode 716. Max Stack (Sorted List)LetsCode · 5:58 · 301 views
The problem
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 elementxonto 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.
You must come up with a solution that supports O(1) for each top call and O(logn) for each other call.
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
105calls will be made topush,pop,top,peekMax, andpopMax. - There will be at least one element in the stack when
pop,top,peekMax, orpopMaxis called.
Approach overview
Problem Overview: Design a stack that behaves like a normal stack but also supports two extra operations: peekMax() to view the maximum element and popMax() to remove the maximum element. If multiple elements share the maximum value, remove the one closest to the top. The challenge is maintaining both stack order and fast access to the current maximum.
Approach 1: Simple Stack Scan (O(n) popMax)
Use a regular stack to store values. push, pop, and top work in O(1) time. For peekMax(), scan the stack to find the maximum element in O(n) time. For popMax(), repeatedly pop elements into a temporary stack until the maximum is found, remove it, then restore the remaining elements. Time complexity becomes O(n) for both max operations with O(n) auxiliary space during the operation.
Approach 2: Two Stacks (O(n) popMax)
Maintain two stacks: the main stack and a max-tracking stack that stores the maximum value at each level. Each push updates the max stack with max(current, previousMax). peekMax() becomes O(1). However, removing the maximum still requires popping elements until the max appears, then pushing them back. That makes popMax() O(n) time and O(n) temporary space. This improves read performance but not deletion of arbitrary maximum elements.
Approach 3: Doubly Linked List + Ordered Map (O(log n))
The optimal design combines a doubly linked list with an ordered map (like TreeMap or balanced BST). The doubly linked list maintains stack order, enabling push, pop, and top in O(1). The ordered map stores values as keys and a list of node references as values. This lets you locate the largest key quickly.
When pushing, insert a node at the tail of the linked list and store its reference in the ordered map. For peekMax(), retrieve the largest key in the map in O(log n) or O(1) depending on implementation. For popMax(), find the largest value, remove the most recently inserted node from its node list, and unlink that node from the doubly linked list. Both structures stay synchronized.
This design efficiently supports all operations: linked list handles stack order while the ordered structure provides fast maximum lookup. Time complexity is O(log n) for push and popMax(), and O(1) for standard stack operations. Space complexity is O(n). It demonstrates how combining a linked list with an ordered set solves conflicting access patterns.
Recommended for interviews: The doubly linked list + ordered map approach is what interviewers typically expect for this hard design problem. Showing the brute-force stack scan first demonstrates understanding of the constraints, while transitioning to the balanced-map design shows strong data structure design skills.
Solution
class Node:
def __init__(self, val=0):
self.val = val
self.prev: Union[Node, None] = None
self.next: Union[Node, None] = None
class DoubleLinkedList:
def __init__(self):
self.head = Node()
self.tail = Node()
self.head.next = self.tail
self.tail.prev = self.head
def append(self, val) -> Node:
node = Node(val)
node.next = self.tail
node.prev = self.tail.prev
self.tail.prev = node
node.prev.next = node
return node
@staticmethod
def remove(node) -> Node:
node.prev.next = node.next
node.next.prev = node.prev
return node
def pop(self) -> Node:
return self.remove(self.tail.prev)
def peek(self):
return self.tail.prev.val
class MaxStack:
def __init__(self):
self.stk = DoubleLinkedList()
self.sl = SortedList(key=lambda x: x.val)
def push(self, x: int) -> None:
node = self.stk.append(x)
self.sl.add(node)
def pop(self) -> int:
node = self.stk.pop()
self.sl.remove(node)
return node.val
def top(self) -> int:
return self.stk.peek()
def peekMax(self) -> int:
return self.sl[-1].val
def popMax(self) -> int:
node = self.sl.pop()
DoubleLinkedList.remove(node)
return node.val
# Your MaxStack object will be instantiated and called as such:
# obj = MaxStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.peekMax()
# param_5 = obj.popMax()class Node {
public int val;
public Node prev, next;
public Node() {
}
public Node(int val) {
this.val = val;
}
}
class DoubleLinkedList {
private final Node head = new Node();
private final Node tail = new Node();
public DoubleLinkedList() {
head.next = tail;
tail.prev = head;
}
public Node append(int val) {
Node node = new Node(val);
node.next = tail;
node.prev = tail.prev;
tail.prev = node;
node.prev.next = node;
return node;
}
public static Node remove(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
return node;
}
public Node pop() {
return remove(tail.prev);
}
public int peek() {
return tail.prev.val;
}
}
class MaxStack {
private DoubleLinkedList stk = new DoubleLinkedList();
private TreeMap<Integer, List<Node>> tm = new TreeMap<>();
public MaxStack() {
}
public void push(int x) {
Node node = stk.append(x);
tm.computeIfAbsent(x, k -> new ArrayList<>()).add(node);
}
public int pop() {
Node node = stk.pop();
List<Node> nodes = tm.get(node.val);
int x = nodes.remove(nodes.size() - 1).val;
if (nodes.isEmpty()) {
tm.remove(node.val);
}
return x;
}
public int top() {
return stk.peek();
}
public int peekMax() {
return tm.lastKey();
}
public int popMax() {
int x = peekMax();
List<Node> nodes = tm.get(x);
Node node = nodes.remove(nodes.size() - 1);
if (nodes.isEmpty()) {
tm.remove(x);
}
DoubleLinkedList.remove(node);
return x;
}
}
/**
* Your MaxStack object will be instantiated and called as such:
* MaxStack obj = new MaxStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.peekMax();
* int param_5 = obj.popMax();
*/class MaxStack {
public:
MaxStack() {
}
void push(int x) {
stk.push_back(x);
tm.insert({x, --stk.end()});
}
int pop() {
auto it = --stk.end();
int ans = *it;
auto mit = --tm.upper_bound(ans);
tm.erase(mit);
stk.erase(it);
return ans;
}
int top() {
return stk.back();
}
int peekMax() {
return tm.rbegin()->first;
}
int popMax() {
auto mit = --tm.end();
auto it = mit->second;
int ans = *it;
tm.erase(mit);
stk.erase(it);
return ans;
}
private:
multimap<int, list<int>::iterator> tm;
list<int> stk;
};
/**
* Your MaxStack object will be instantiated and called as such:
* MaxStack* obj = new MaxStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->peekMax();
* int param_5 = obj->popMax();
*/Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Simple Stack Scan | push/pop O(1), peekMax O(n), popMax O(n) | O(n) | Good for understanding the problem or quick prototypes |
| Two Stacks (Stack + Max Stack) | push/pop/top O(1), peekMax O(1), popMax O(n) | O(n) | When frequent max lookups are needed but popMax is rare |
| Doubly Linked List + Ordered Map | push O(log n), pop O(1), peekMax O(log n), popMax O(log n) | O(n) | Optimal interview solution supporting fast stack and max operations |
Frequently asked questions
Is Max Stack easy or hard?
Max Stack is classified as a hard problem because it requires designing a custom data structure rather than applying a standard algorithm. The challenge comes from supporting both stack behavior and efficient maximum element removal at the same time.
Max Stack Python/Java solution
In Python, the structure is often implemented using a doubly linked list plus a sorted dictionary or heap-like structure. In Java, TreeMap is commonly used to maintain ordered keys while storing references to linked list nodes. Both implementations achieve O(log n) push and popMax operations.
How to solve Max Stack in O(n)?
A simpler approach uses a single stack and scans it whenever the maximum element is needed. peekMax iterates through the stack to find the largest value, and popMax temporarily moves elements to another stack until the maximum appears. These operations take O(n) time but keep the implementation straightforward.
What is the best approach for Max Stack?
The most efficient design uses a doubly linked list combined with an ordered map (TreeMap or balanced BST). The linked list maintains stack order while the ordered map keeps values sorted for fast maximum lookup. This allows push in O(log n), pop in O(1), peekMax in O(log n), and popMax in O(log n).
Is Max Stack asked at Google/Amazon/Meta?
Max Stack is a classic system design style data structure problem that appears in interviews at companies like Amazon, Google, and Meta. It tests your ability to combine multiple data structures to satisfy conflicting requirements such as stack order and fast maximum lookup.
What data structure is used in Max Stack?
The optimal solution combines three structures: a stack order maintained by a doubly linked list, an ordered map or TreeMap to keep values sorted, and lists of node references for duplicates. This hybrid design allows efficient removal of the maximum element without scanning the entire stack.
What is the time complexity of Max Stack?
In the optimal implementation using a doubly linked list and ordered map, push and popMax take O(log n) time due to map updates, while pop and top are O(1). peekMax is typically O(log n) depending on how the maximum key is retrieved. Space complexity is O(n) because every element is stored in both structures.
