Talentd
FleetCode
Make and follow sheets.Sign in

Max Stack solution and explanation

HardDesignDoubly-Linked ListLinked ListOrdered Set5 min read45.9% acceptanceUpdated 23 Mar 2026Premium elsewhere, free here
Implement A Max Stack - A Stack With A .max() API (Similar To "Min Stack" on LeetCode)Back To Back SWE · 16:37 · 24K viewsWatch on YouTube
9 more videos on this problem
  1. Implement A Max Stack - A Stack With A .max() API (Similar To "Min Stack" on LeetCode)Back To Back SWE · 16:37 · 24K views
  2. LeetCode 716 | Max Stackleetuition · 17:20 · 12K views
  3. Mock Coding Interview: Pivot Index and Max Stack (Python)abrar · 51:47 · 10K views
  4. Max Stack | Leetcode 716Pepcoding · 30:45 · 5K views
  5. Max Stack | LeetCode 716 (Hard)Destination FAANG · 12:38 · 4K views
  6. Max Stack Optimal approach | O(log(n)) | Leetcode 716Pepcoding · 51:57 · 4K views
  7. Leetcode 716. Max Stack | Python Solution | LinkedIn Interview QuestionDonald Dang · 7:50 · 3K views
  8. Leetcode 716(easy). Max Stack【Stack/Design】中文AndroidBabies安卓大宝贝们 · 19:06 · 1K views
  9. Max Stack - LeetCode 716 - JavaScott Hacks Code · 15:21 · 682 views
  10. 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 element x 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.

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 105 calls will be made to push, pop, top, peekMax, and popMax.
  • There will be at least one element in the stack when pop, top, peekMax, or popMax is 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();
 */
Try this approach in the editor

Complexity analysis

ApproachTimeSpaceWhen to Use
Simple Stack Scanpush/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 Mappush 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.

Comments

Now write it yourselfClose this page and solve Max Stack in the editor, without looking.
Practise Max Stack