Doubly linked list + TreeMap
Time O(log n) per call Space O(n)push adds a node at the list's tail and to map[value]. pop removes the tail node and its map entry. popMax takes the last node of map.lastEntry() and unlinks it from the list.
import java.util.*;
class MaxStack {
private static class Node {
int val;
Node prev, next;
Node(int val) { this.val = val; }
}
private final Node head = new Node(0), tail = new Node(0); // sentinels
private final TreeMap<Integer, List<Node>> byValue = new TreeMap<>();
public MaxStack() { head.next = tail; tail.prev = head; }
public void push(int x) {
Node n = new Node(x);
n.prev = tail.prev; n.next = tail;
tail.prev.next = n; tail.prev = n;
byValue.computeIfAbsent(x, k -> new ArrayList<>()).add(n);
}
public int pop() {
Node n = tail.prev;
unlink(n);
List<Node> same = byValue.get(n.val);
same.remove(same.size() - 1); // the top-most node with this value
if (same.isEmpty()) byValue.remove(n.val);
return n.val;
}
public int top() { return tail.prev.val; }
public int peekMax() { return byValue.lastKey(); }
public int popMax() {
int max = byValue.lastKey();
List<Node> same = byValue.get(max);
Node n = same.remove(same.size() - 1);
if (same.isEmpty()) byValue.remove(max);
unlink(n);
return max;
}
private void unlink(Node n) { n.prev.next = n.next; n.next.prev = n.prev; }
}Verdict: Every operation stays logarithmic.