716. Max Stack 🔒
Problem Description
You need to design a max stack data structure that combines the functionality of a regular stack with the ability to efficiently retrieve and remove the maximum element.
The MaxStack
class should support the following operations:
-
MaxStack()
: Initialize an empty stack object. -
push(int x)
: Add elementx
to the top of the stack. -
pop()
: Remove and return the element from the top of the stack (most recently added element). -
top()
: Return the element at the top of the stack without removing it. -
peekMax()
: Return the maximum element currently in the stack without removing it. -
popMax()
: Remove and return the maximum element from the stack. If multiple elements have the same maximum value, remove the one that is closest to the top of the stack (the most recently added among the maximum elements).
Performance Requirements:
- The
top()
operation must run inO(1)
time - All other operations (
push
,pop
,peekMax
,popMax
) must run inO(log n)
time
The challenge is to maintain both stack ordering (LIFO - Last In First Out) and efficient access to the maximum element. When popMax()
is called, you need to be able to remove an element that might not be at the top of the stack while preserving the relative order of all other elements.
For example, if you push [5, 1, 5]
in that order, the stack from bottom to top is [5, 1, 5]
. Calling popMax()
should remove and return the top 5
(not the bottom one), leaving the stack as [5, 1]
.
Intuition
The key insight is that we need to maintain two different orderings of the same elements simultaneously: the stack order (insertion order) and the value order (for finding maximum). This immediately suggests using two data structures working together.
For a regular stack, we could use a simple array or linked list. However, since popMax()
requires removing an element from potentially anywhere in the stack (not just the top), we need a data structure that supports efficient removal from any position. This leads us to consider a doubly-linked list, where any node can be removed in O(1)
time if we have a reference to it.
The second challenge is maintaining quick access to the maximum element. A sorted container like a balanced BST or a sorted list would allow us to find the maximum in O(1)
or O(log n)
time. But here's the clever part: instead of storing values in both structures separately, we can store node references in the sorted structure that point to nodes in our doubly-linked list.
This creates a beautiful synergy:
- When we
push
, we create a node in the doubly-linked list and add a reference to that same node in our sorted structure - When we
pop
, we remove the node from the tail of the linked list and also remove its reference from the sorted structure - When we
popMax()
, we get the node reference from the sorted structure, remove it from there, and also unlink it from the doubly-linked list
The sorted structure (using Python's SortedList
) maintains nodes sorted by their values, so the maximum is always at the end. When multiple nodes have the same maximum value, the SortedList
maintains them in insertion order, ensuring that popMax()
naturally removes the most recently added maximum.
This design elegantly solves the problem of maintaining both orderings while allowing efficient operations on both ends - stack operations on the linked list side and max operations through the sorted structure.
Learn more about Stack and Linked List patterns.
Solution Approach
The solution uses a combination of a doubly-linked list and a sorted list to achieve the required time complexities.
Data Structure Design
1. Node Class:
class Node:
def __init__(self, val=0):
self.val = val
self.prev = None
self.next = None
Each node stores a value and pointers to both previous and next nodes, enabling bidirectional traversal and O(1)
removal.
2. DoubleLinkedList Class: The doubly-linked list uses sentinel nodes (dummy head and tail) to simplify edge cases:
head
andtail
are dummy nodes that always exist- Actual elements are inserted between
head
andtail
- The stack's top is always at
tail.prev
Key methods:
append(val)
: Creates a new node and inserts it just before the tail. Returns the node reference for use in the sorted list.remove(node)
: Unlinks a node from the list by updating its neighbors' pointers (node.prev.next = node.next
andnode.next.prev = node.prev
)pop()
: Removes and returns the node attail.prev
(top of stack)peek()
: Returns the value attail.prev
without removal
3. MaxStack Class: Combines two structures:
self.stk
: ADoubleLinkedList
maintaining stack orderself.sl
: ASortedList
containing node references, sorted by node values
Operation Implementation
push(x)
:
- Create and append a new node to the doubly-linked list
- Add the same node reference to the sorted list
- The sorted list maintains nodes in ascending order by their values
pop()
:
- Remove the last node from the doubly-linked list (stack top)
- Remove the same node reference from the sorted list
- Return the node's value
top()
:
Simply return tail.prev.val
from the doubly-linked list - this is O(1)
since we're just accessing a value.
peekMax()
:
Return the value of the last node in the sorted list (self.sl[-1].val
). Since the list is sorted, the maximum is always at the end.
popMax()
:
- Pop the last node from the sorted list (the maximum)
- Remove this same node from the doubly-linked list using the static
remove
method - Return the node's value
Time Complexity Analysis
top()
:O(1)
- Direct access to the tail's previous nodepush()
:O(log n)
- Dominated by sorted list insertionpop()
:O(log n)
- Dominated by sorted list removalpeekMax()
:O(1)
- Direct access to the last element in sorted listpopMax()
:O(log n)
- Sorted list removal isO(log n)
, doubly-linked list removal isO(1)
The key insight is that by storing node references rather than values in the sorted list, we can maintain both orderings efficiently and perform cross-structure operations seamlessly.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a sequence of operations to see how the two data structures work together:
Initial State:
- Doubly-linked list:
[head] ↔ [tail]
(empty) - Sorted list:
[]
(empty)
Operation 1: push(3)
- Create a new node with value 3
- Insert it into the doubly-linked list between head and tail
- Add the node reference to the sorted list
- Doubly-linked list:
[head] ↔ [Node(3)] ↔ [tail]
- Sorted list:
[Node(3)]
(sorted by value)
Operation 2: push(5)
- Create a new node with value 5
- Insert it before tail (after Node(3))
- Add to sorted list, which maintains sorted order
- Doubly-linked list:
[head] ↔ [Node(3)] ↔ [Node(5)] ↔ [tail]
- Sorted list:
[Node(3), Node(5)]
(sorted by value)
Operation 3: push(1)
- Create Node(1) and insert before tail
- Sorted list inserts it at the beginning due to its value
- Doubly-linked list:
[head] ↔ [Node(3)] ↔ [Node(5)] ↔ [Node(1)] ↔ [tail]
- Sorted list:
[Node(1), Node(3), Node(5)]
(sorted by value) - Stack order (bottom to top): [3, 5, 1]
Operation 4: peekMax()
- Look at the last element in sorted list:
Node(5)
- Return its value: 5
- No changes to either structure
Operation 5: popMax()
- Remove last node from sorted list:
Node(5)
- Use the node reference to unlink it from the doubly-linked list
- Return value 5
- Doubly-linked list:
[head] ↔ [Node(3)] ↔ [Node(1)] ↔ [tail]
- Sorted list:
[Node(1), Node(3)]
- Stack now contains: [3, 1]
Operation 6: top()
- Access
tail.prev
which isNode(1)
- Return value: 1
- This is O(1) as we're just following a pointer
Operation 7: push(5)
again
-
Create new Node(5) and add to both structures
-
Doubly-linked list:
[head] ↔ [Node(3)] ↔ [Node(1)] ↔ [Node(5)] ↔ [tail]
-
Sorted list:
[Node(1), Node(3), Node(5)]
Operation 8: push(5)
once more
-
Another Node(5) is created (different node object, same value)
-
Doubly-linked list:
[head] ↔ [Node(3)] ↔ [Node(1)] ↔ [Node(5)₁] ↔ [Node(5)₂] ↔ [tail]
-
Sorted list:
[Node(1), Node(3), Node(5)₁, Node(5)₂]
-
Note: The sorted list maintains insertion order for equal values
Operation 9: popMax()
-
Removes
Node(5)₂
(the most recently added 5) -
This demonstrates that when multiple maximums exist, we remove the one closest to the top
-
Doubly-linked list:
[head] ↔ [Node(3)] ↔ [Node(1)] ↔ [Node(5)₁] ↔ [tail]
-
Sorted list:
[Node(1), Node(3), Node(5)₁]
The beauty of this approach is that both structures share the same node objects. When we remove a node from one structure, we can immediately locate and remove it from the other using the node reference, maintaining consistency between both orderings efficiently.
Solution Implementation
1from typing import Optional
2from sortedcontainers import SortedList
3
4
5class Node:
6 """Node for doubly linked list."""
7
8 def __init__(self, val: int = 0) -> None:
9 self.val: int = val
10 self.prev: Optional['Node'] = None
11 self.next: Optional['Node'] = None
12
13
14class DoublyLinkedList:
15 """Doubly linked list with sentinel nodes for efficient operations."""
16
17 def __init__(self) -> None:
18 # Create sentinel head and tail nodes
19 self.head: Node = Node()
20 self.tail: Node = Node()
21 # Connect sentinels
22 self.head.next = self.tail
23 self.tail.prev = self.head
24
25 def append(self, val: int) -> Node:
26 """
27 Append a new node with given value to the end of the list.
28 Returns the newly created node.
29 """
30 node = Node(val)
31 # Insert before tail sentinel
32 node.next = self.tail
33 node.prev = self.tail.prev
34 self.tail.prev = node
35 node.prev.next = node
36 return node
37
38 @staticmethod
39 def remove(node: Node) -> Node:
40 """
41 Remove a node from the list by updating its neighbors' pointers.
42 Returns the removed node.
43 """
44 node.prev.next = node.next
45 node.next.prev = node.prev
46 return node
47
48 def pop(self) -> Node:
49 """
50 Remove and return the last node (before tail sentinel).
51 """
52 return self.remove(self.tail.prev)
53
54 def peek(self) -> int:
55 """
56 Return the value of the last node without removing it.
57 """
58 return self.tail.prev.val
59
60
61class MaxStack:
62 """
63 Stack data structure that supports retrieving the maximum element.
64 Supports O(log n) push, pop, top, peekMax, and popMax operations.
65 """
66
67 def __init__(self) -> None:
68 # Doubly linked list to maintain stack order
69 self.stack: DoublyLinkedList = DoublyLinkedList()
70 # Sorted list to efficiently track maximum elements
71 # Stores references to nodes, sorted by their values
72 self.sorted_list: SortedList = SortedList(key=lambda x: x.val)
73
74 def push(self, x: int) -> None:
75 """
76 Push element x onto the stack.
77 """
78 # Add node to the end of the stack
79 node = self.stack.append(x)
80 # Add node reference to sorted list for max tracking
81 self.sorted_list.add(node)
82
83 def pop(self) -> int:
84 """
85 Remove and return the top element from the stack.
86 """
87 # Remove from stack
88 node = self.stack.pop()
89 # Remove from sorted list
90 self.sorted_list.remove(node)
91 return node.val
92
93 def top(self) -> int:
94 """
95 Get the top element without removing it.
96 """
97 return self.stack.peek()
98
99 def peekMax(self) -> int:
100 """
101 Get the maximum element in the stack without removing it.
102 """
103 # Last element in sorted list is the maximum
104 return self.sorted_list[-1].val
105
106 def popMax(self) -> int:
107 """
108 Remove and return the maximum element from the stack.
109 """
110 # Get and remove max node from sorted list
111 node = self.sorted_list.pop()
112 # Remove the same node from the stack
113 DoublyLinkedList.remove(node)
114 return node.val
115
116
117# Your MaxStack object will be instantiated and called as such:
118# obj = MaxStack()
119# obj.push(x)
120# param_2 = obj.pop()
121# param_3 = obj.top()
122# param_4 = obj.peekMax()
123# param_5 = obj.popMax()
124
1/**
2 * Node class for doubly linked list implementation
3 */
4class Node {
5 public int val;
6 public Node prev;
7 public Node next;
8
9 public Node() {
10 }
11
12 public Node(int val) {
13 this.val = val;
14 }
15}
16
17/**
18 * Doubly linked list implementation with sentinel nodes
19 * Used as the underlying stack structure
20 */
21class DoubleLinkedList {
22 private final Node head; // Sentinel head node
23 private final Node tail; // Sentinel tail node
24
25 public DoubleLinkedList() {
26 head = new Node();
27 tail = new Node();
28 head.next = tail;
29 tail.prev = head;
30 }
31
32 /**
33 * Append a new node with given value to the end of the list (before tail)
34 * @param val Value to append
35 * @return The newly created node
36 */
37 public Node append(int val) {
38 Node node = new Node(val);
39 node.next = tail;
40 node.prev = tail.prev;
41 tail.prev = node;
42 node.prev.next = node;
43 return node;
44 }
45
46 /**
47 * Remove a node from the list
48 * @param node Node to remove
49 * @return The removed node
50 */
51 public static Node remove(Node node) {
52 node.prev.next = node.next;
53 node.next.prev = node.prev;
54 return node;
55 }
56
57 /**
58 * Remove and return the last node (stack pop operation)
59 * @return The removed node
60 */
61 public Node pop() {
62 return remove(tail.prev);
63 }
64
65 /**
66 * Get the value of the last node without removing it (stack peek operation)
67 * @return Value of the last node
68 */
69 public int peek() {
70 return tail.prev.val;
71 }
72}
73
74/**
75 * MaxStack implementation supporting O(log n) max operations
76 * Uses a doubly linked list for stack operations and a TreeMap for tracking maximum values
77 */
78class MaxStack {
79 private DoubleLinkedList stack; // Doubly linked list acting as stack
80 private TreeMap<Integer, List<Node>> valueToNodes; // TreeMap mapping values to their nodes
81
82 public MaxStack() {
83 stack = new DoubleLinkedList();
84 valueToNodes = new TreeMap<>();
85 }
86
87 /**
88 * Push element x onto stack
89 * @param x Element to push
90 */
91 public void push(int x) {
92 // Add node to the stack
93 Node node = stack.append(x);
94 // Add node reference to TreeMap for O(log n) max operations
95 valueToNodes.computeIfAbsent(x, k -> new ArrayList<>()).add(node);
96 }
97
98 /**
99 * Remove and return the element on top of the stack
100 * @return The top element
101 */
102 public int pop() {
103 // Remove node from stack
104 Node node = stack.pop();
105 // Remove node reference from TreeMap
106 List<Node> nodes = valueToNodes.get(node.val);
107 int value = nodes.remove(nodes.size() - 1).val;
108 // Clean up empty list from TreeMap
109 if (nodes.isEmpty()) {
110 valueToNodes.remove(node.val);
111 }
112 return value;
113 }
114
115 /**
116 * Get the element on the top of the stack without removing it
117 * @return The top element
118 */
119 public int top() {
120 return stack.peek();
121 }
122
123 /**
124 * Retrieve the maximum element in the stack without removing it
125 * @return The maximum element
126 */
127 public int peekMax() {
128 // TreeMap's lastKey() gives us the maximum value in O(log n)
129 return valueToNodes.lastKey();
130 }
131
132 /**
133 * Remove and return the maximum element in the stack
134 * @return The maximum element
135 */
136 public int popMax() {
137 // Get the maximum value
138 int maxValue = peekMax();
139 // Get list of nodes with this value
140 List<Node> nodes = valueToNodes.get(maxValue);
141 // Remove the most recent node with max value (last in the list)
142 Node node = nodes.remove(nodes.size() - 1);
143 // Clean up empty list from TreeMap
144 if (nodes.isEmpty()) {
145 valueToNodes.remove(maxValue);
146 }
147 // Remove the node from the doubly linked list
148 DoubleLinkedList.remove(node);
149 return maxValue;
150 }
151}
152
153/**
154 * Your MaxStack object will be instantiated and called as such:
155 * MaxStack obj = new MaxStack();
156 * obj.push(x);
157 * int param_2 = obj.pop();
158 * int param_3 = obj.top();
159 * int param_4 = obj.peekMax();
160 * int param_5 = obj.popMax();
161 */
162
1class MaxStack {
2public:
3 MaxStack() {
4 // Constructor - no initialization needed as containers are empty by default
5 }
6
7 void push(int x) {
8 // Add element to the back of the list (acts as stack top)
9 stack.push_back(x);
10
11 // Store the value and its position in the list in the multimap
12 // multimap automatically sorts by key (value), allowing O(log n) max retrieval
13 valueToPosition.insert({x, --stack.end()});
14 }
15
16 int pop() {
17 // Get iterator to the last element (stack top)
18 auto stackTopIter = --stack.end();
19 int topValue = *stackTopIter;
20
21 // Find the corresponding entry in multimap
22 // upper_bound returns iterator to first element > topValue
23 // Decrementing gives us the last occurrence of topValue
24 auto mapIter = --valueToPosition.upper_bound(topValue);
25
26 // Remove from both data structures
27 valueToPosition.erase(mapIter);
28 stack.erase(stackTopIter);
29
30 return topValue;
31 }
32
33 int top() {
34 // Return the last element without removing it
35 return stack.back();
36 }
37
38 int peekMax() {
39 // rbegin() points to the largest element in the sorted multimap
40 return valueToPosition.rbegin()->first;
41 }
42
43 int popMax() {
44 // Get iterator to the maximum element in multimap (last element)
45 auto maxMapIter = --valueToPosition.end();
46
47 // Get the list iterator stored in the multimap entry
48 auto stackIter = maxMapIter->second;
49 int maxValue = *stackIter;
50
51 // Remove from both data structures
52 valueToPosition.erase(maxMapIter);
53 stack.erase(stackIter);
54
55 return maxValue;
56 }
57
58private:
59 // Multimap maintains elements sorted by value
60 // Key: element value, Value: iterator pointing to element's position in list
61 multimap<int, list<int>::iterator> valueToPosition;
62
63 // List acts as the stack (using back as top)
64 // List is used instead of vector to maintain iterator validity after erasure
65 list<int> stack;
66};
67
68/**
69 * Your MaxStack object will be instantiated and called as such:
70 * MaxStack* obj = new MaxStack();
71 * obj->push(x);
72 * int param_2 = obj->pop();
73 * int param_3 = obj->top();
74 * int param_4 = obj->peekMax();
75 * int param_5 = obj->popMax();
76 */
77
1// List node for doubly linked list implementation
2class ListNode {
3 val: number;
4 prev: ListNode | null;
5 next: ListNode | null;
6
7 constructor(val: number) {
8 this.val = val;
9 this.prev = null;
10 this.next = null;
11 }
12}
13
14// Doubly linked list acts as the stack (using tail as top)
15// Using linked list to maintain reference validity after removal
16let head: ListNode | null = null;
17let tail: ListNode | null = null;
18
19// Map maintains elements sorted by value
20// Key: element value, Value: array of nodes with that value
21// Using array to handle duplicate values
22let valueToNodes: Map<number, ListNode[]> = new Map();
23
24// Sorted array of unique values for O(log n) max retrieval
25let sortedValues: number[] = [];
26
27function MaxStack(): void {
28 // Initialize data structures
29 head = null;
30 tail = null;
31 valueToNodes = new Map();
32 sortedValues = [];
33}
34
35function push(x: number): void {
36 // Create new node for the value
37 const newNode = new ListNode(x);
38
39 // Add node to the end of linked list (acts as stack top)
40 if (!tail) {
41 head = tail = newNode;
42 } else {
43 tail.next = newNode;
44 newNode.prev = tail;
45 tail = newNode;
46 }
47
48 // Store the node reference in the map
49 if (!valueToNodes.has(x)) {
50 valueToNodes.set(x, []);
51 // Insert value into sorted array maintaining order
52 const insertIndex = binarySearchInsert(sortedValues, x);
53 sortedValues.splice(insertIndex, 0, x);
54 }
55 valueToNodes.get(x)!.push(newNode);
56}
57
58function pop(): number {
59 // Get the last element (stack top)
60 const topNode = tail!;
61 const topValue = topNode.val;
62
63 // Remove node from linked list
64 if (topNode.prev) {
65 topNode.prev.next = null;
66 tail = topNode.prev;
67 } else {
68 head = tail = null;
69 }
70
71 // Remove node reference from map
72 const nodes = valueToNodes.get(topValue)!;
73 nodes.pop(); // Remove the last occurrence
74
75 // Clean up if no more nodes with this value
76 if (nodes.length === 0) {
77 valueToNodes.delete(topValue);
78 const index = sortedValues.indexOf(topValue);
79 sortedValues.splice(index, 1);
80 }
81
82 return topValue;
83}
84
85function top(): number {
86 // Return the last element without removing it
87 return tail!.val;
88}
89
90function peekMax(): number {
91 // Return the largest element from sorted array
92 return sortedValues[sortedValues.length - 1];
93}
94
95function popMax(): number {
96 // Get the maximum value
97 const maxValue = sortedValues[sortedValues.length - 1];
98
99 // Get the last node with maximum value
100 const nodes = valueToNodes.get(maxValue)!;
101 const maxNode = nodes[nodes.length - 1];
102
103 // Remove node from linked list
104 if (maxNode.prev) {
105 maxNode.prev.next = maxNode.next;
106 } else {
107 head = maxNode.next;
108 }
109
110 if (maxNode.next) {
111 maxNode.next.prev = maxNode.prev;
112 } else {
113 tail = maxNode.prev;
114 }
115
116 // Remove node reference from map
117 nodes.pop();
118
119 // Clean up if no more nodes with this value
120 if (nodes.length === 0) {
121 valueToNodes.delete(maxValue);
122 sortedValues.pop();
123 }
124
125 return maxValue;
126}
127
128// Helper function for binary search insertion
129function binarySearchInsert(arr: number[], target: number): number {
130 let left = 0;
131 let right = arr.length;
132
133 while (left < right) {
134 const mid = Math.floor((left + right) / 2);
135 if (arr[mid] < target) {
136 left = mid + 1;
137 } else {
138 right = mid;
139 }
140 }
141
142 return left;
143}
144
Time and Space Complexity
Time Complexity:
push(x)
:O(log n)
- Appending to the doubly linked list isO(1)
, but adding to the SortedList requiresO(log n)
for binary search insertionpop()
:O(n)
- Removing from the tail of the doubly linked list isO(1)
, but removing from the SortedList requiresO(n)
as it needs to find and shift elements after removaltop()
:O(1)
- Simply returns the value at the tail of the doubly linked listpeekMax()
:O(1)
- Accessing the last element in the SortedList is constant timepopMax()
:O(n)
- Popping from the SortedList isO(n)
due to element shifting, and removing from the doubly linked list isO(1)
Space Complexity:
O(n)
wheren
is the number of elements in the stack- The doubly linked list stores
n
nodes - The SortedList stores references to the same
n
nodes - Each node contains a value and two pointers (prev and next)
- Total space is
O(n) + O(n) = O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Storing Values Instead of Node References in SortedList
One of the most common mistakes is storing just the values in the sorted list rather than the actual node references. This breaks the connection between the two data structures and makes it impossible to efficiently remove the correct element during popMax()
.
Incorrect approach:
class MaxStack:
def __init__(self):
self.stack = DoublyLinkedList()
self.sorted_list = SortedList() # Stores values, not nodes!
def push(self, x):
self.stack.append(x)
self.sorted_list.add(x) # Wrong: adding value instead of node
def popMax(self):
max_val = self.sorted_list.pop()
# Problem: How do we find and remove the RIGHT occurrence
# of max_val from the stack? If there are duplicates,
# we might remove the wrong one!
Why this fails: When multiple elements have the same value, you can't determine which specific node to remove from the doubly-linked list. The problem requires removing the most recently added maximum (closest to top), but with only values, you'd have to traverse the entire stack to find it, breaking the O(log n) time complexity requirement.
Solution: Always store node references in the sorted list, using a key function for sorting:
self.sorted_list = SortedList(key=lambda x: x.val)
2. Forgetting to Handle Bidirectional Updates
Another pitfall is updating only one data structure while forgetting the other, leading to inconsistency.
Incorrect approach:
def pop(self):
node = self.stack.pop()
# Forgot to remove from sorted_list!
return node.val
Why this fails: The sorted list will still contain a reference to a node that's no longer in the stack. Future peekMax()
or popMax()
operations might try to access this orphaned node, causing incorrect results or errors.
Solution: Always update both data structures in tandem:
def pop(self):
node = self.stack.pop()
self.sorted_list.remove(node) # Don't forget this!
return node.val
3. Using Built-in List Instead of Doubly-Linked List
Some might try to use Python's built-in list for the stack, which seems simpler but creates problems.
Incorrect approach:
class MaxStack:
def __init__(self):
self.stack = [] # Using regular list
self.sorted_list = SortedList()
def popMax(self):
max_val = self.sorted_list.pop()
# Problem: Removing from middle of list is O(n)!
idx = self.stack.index(max_val) # O(n) search
return self.stack.pop(idx) # O(n) removal
Why this fails: Removing an element from an arbitrary position in a Python list requires O(n) time because all subsequent elements must be shifted. This violates the O(log n) requirement for popMax()
.
Solution: Use a doubly-linked list where node removal from any position is O(1) once you have the node reference.
4. Not Handling Duplicate Values Correctly
When the stack contains duplicate maximum values, the implementation must remove the correct one (most recently added).
Incorrect approach:
def popMax(self):
max_val = self.sorted_list[-1].val
# Wrong: This might remove the first occurrence, not the last
for node in self.sorted_list:
if node.val == max_val:
self.sorted_list.remove(node)
DoublyLinkedList.remove(node)
return max_val
Why this fails: This removes the first occurrence of the maximum value encountered in the sorted list, which might not be the most recently added one.
Solution: The sorted list should maintain stable ordering for equal elements (most implementations do this by default), and you should always remove from the end:
def popMax(self):
node = self.sorted_list.pop() # Removes the last (most recent) max
DoublyLinkedList.remove(node)
return node.val
What is an advantages of top-down dynamic programming vs bottom-up dynamic programming?
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Linked List Cycle Given a linked list with potentially a loop determine whether the linked list from the first node contains a cycle in it For bonus points do this with constant space Parameters nodes The first node of a linked list with potentially a loop Result Whether there is a loop contained
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!