Introduction to Monotonic Stack
A monotonic stack is a stack that a caller keeps sorted (increasing or decreasing, bottom to top) by popping any elements that would break the order before pushing — because each element is compared against, and permanently removes, only the elements it beats, the total number of comparisons across the whole array is bounded by the number of pushes plus pops, giving O(n) instead of the O(n²) of comparing every pair.
Recognize the pattern
- The phrase "next/previous greater (or smaller) element" for every index.
- You need, for each element, the nearest element to its left/right satisfying an order relation.
- Histogram / "largest rectangle", "trapping rain water", "stock span", "daily temperatures" — anything reducible to nearest-bigger/smaller-neighbor.
- A naive solution is a double loop comparing each element against all others in one direction.
Brute force → optimal
Brute force: for each index i, scan rightward (or leftward) until you find the first element satisfying the relation. Worst case (strictly decreasing array, looking for next greater) every scan runs to the end: O(n²) time, O(1) extra space.
Optimal (monotonic stack): walk the array once, maintaining a stack of indices whose values are still "undecided" (no next-greater found yet), kept in decreasing order of value from bottom to top. For each new value, pop every stacked index whose value is smaller — the new value IS their answer. Then push the current index. O(n) time, O(n) space.
Complexity, derived
Let n = array length. Each index is pushed onto the stack exactly once (in the single left-to-right pass) and can therefore be popped at most once over the whole run — it never gets pushed again after popping. So total pushes = n, total pops ≤ n. The while-loop body inside the for-loop runs once per pop, so summed over all iterations the inner loop body executes ≤ n times, not n times per outer iteration. Total work = O(n) pushes + O(n) pops = O(n) time. The stack holds at most n indices → O(n) space (plus the O(n) output array).
Worked example — Next Greater Element
Array: [2, 1, 2, 4, 3]. Find next greater element to the right for each index; stack holds indices, kept so values are decreasing bottom→top.
| i | val | action | stack after (indices) | NGE resolved |
|---|---|---|---|---|
| 0 | 2 | push 0 | [0] | — |
| 1 | 1 | 1<val@0(2), push 1 | [0,1] | — |
| 2 | 2 | pop 1 (1<2)→NGE[1]=2; top now 0 (val 2, not < 2, stop); push 2 | [0,2] | NGE[1]=2 |
| 3 | 4 | pop 2(2<4)→NGE[2]=4; pop 0(2<4)→NGE[0]=4; push 3 | [3] | NGE[2]=4, NGE[0]=4 |
| 4 | 3 | 3<val@3(4), push 4 | [3,4] | — |
End of array: remaining stack indices [3,4] get NGE = -1 (none found). Final: [4, 2, 4, -1, -1].
Java — Next Greater Element
int[] nextGreater(int[] nums) {
int n = nums.length;
int[] res = new int[n];
Arrays.fill(res, -1);
Deque<Integer> stack = new ArrayDeque<>(); // holds indices
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && nums[stack.peek()] < nums[i]) {
res[stack.pop()] = nums[i];
}
stack.push(i);
}
return res;
}Pitfalls
- Storing values instead of indices — you then can't report position or distance (e.g. "daily temperatures" needs
i - stack.pop()). - Off-by-one on the comparison operator:
<vs<=changes whether equal elements pop each other, which matters for problems needing the strictly next greater/smaller vs. tie-breaking rules. - Forgetting to drain the stack after the loop — leftover indices need a documented fallback answer (commonly -1).
- Confusing which direction you scan: next-greater-to-the-right needs a left-to-right pass; previous-greater-to-the-left needs the same pass but stack semantics answer a different query per element.
When to use / when not — trade-offs
Use a monotonic stack when the problem needs, for every element, the nearest element in one direction satisfying an order relation — it turns O(n²) pairwise comparison into O(n). Compare with alternatives:
- Sliding window / two pointers: better when the invariant is about a contiguous range's sum/count, not an order relation between elements — monotonic stack does not track sums.
- Sorting + index mapping: works for some "find element with property X" problems but destroys original order, so it can't directly answer "next" queries; also costs O(n log n).
- Segment tree / sparse table (range max): handles arbitrary range-max queries and updates, but is O(n log n) to build and overkill when you only need the single nearest greater neighbor per element, which the O(n) stack answers directly.
- Monotonic deque: when the extreme must also expire out of a sliding FIFO window (window max/min), you need eviction at both ends — that's the monotonic deque, not a plain stack.
Takeaways
- The stack stays ordered because every push first evicts elements that the new value dominates, giving amortized O(n) since each index is pushed and popped at most once.
- Store indices, not values, so you can compute distances or write into a results array by position.
- Increasing stack → find next/previous smaller; decreasing stack → find next/previous greater (the popped elements are the ones "defeated" by the incoming element).
Recall: Why is the total runtime O(n) even though there's a while-loop nested inside a for-loop?
L0 · A monotonic stack maintains elements in a strict sorted order (increasing or decreasing) to solve next-greater-element problems in O(n) time.
L1 · ⑤ Adversary/Edge — “The array contains multiple duplicate values. Should the monotonic stack store duplicate values or discard/process them?”
Trap: Always discard duplicate elements as they violate monotonicity.
Bar: Store indices on the stack instead of literal values; check monotonicity using arr[stack.peek()] < arr[i] (strict) or <= (non-strict) to handle duplicates correctly without losing index alignment. Monotonic Stack
L2 · ② Failure — “You run the next-greater-element algorithm on a strictly decreasing array (e.g. [5, 4, 3, 2, 1]). What is the space complexity of the stack?”
Trap: O(1) space because no element finds a greater neighbor.
Bar: Since no element finds a greater neighbor, no element is popped; all N elements are pushed, creating a worst-case O(N) space footprint, though the time complexity remains O(N). Monotonic Stack
L3 · ③ Scale — “The input is a circular array (the next element of the last element is the first element). How do you resolve next-greater-elements in O(N) time and O(N) space?”
Trap: Duplicate the array to make it size 2N, then run standard monotonic stack.
Bar: Duplicating the array allocates O(N) physical memory; simulate a 2N array by running the loop from 0 to 2N-1 and accessing the array using modulo indexing (i % N), preserving space limits. Monotonic Stack
L4 · ① Concurrency — “Can you parallelize a monotonic stack algorithm across multiple CPU cores for a single array?”
Trap: Divide the array into chunks and run the monotonic stack on each chunk concurrently.
Bar: Monotonic stack state depends on global scan history; partition the array, compute monotonic stacks locally on each chunk, and resolve boundary crossings in a merging phase. Monotonic Stack
L5 · ⑥ Cost/Simplicity — “Why use a monotonic stack when a nested loop (brute force) is easier to implement?”
Trap: Monotonic stack is always faster.
Bar: For small inputs (N < 20), the nested loop has better instruction cache locality and no stack allocation overhead; reach for monotonic stack when N is large and you need O(N) time complexity. Monotonic Stack
The floor keeps dropping: How do you modify a monotonic stack to find the largest rectangular area in a histogram in one pass?
Self-locate: died at L1 → you present mid-level; L4+ → staff signal.
Facing any new concept? Hit it with the six: concurrent? failing? at 100×? over time? adversarial? worth the cost? — that's the interviewer's whole playbook.
Pattern popularized through LeetCode's Next Greater Element / Daily Temperatures / Largest Rectangle in Histogram problem family; complexity argument via amortized analysis (aggregate method), as in CLRS Ch. 17.
🤖 Don't fully get this? Learn it with Claude
Stuck on Introduction to Monotonic Stack? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **Introduction to Monotonic Stack** (DSA) and want to truly understand it. Explain Introduction to Monotonic Stack from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Socratic — adapts to where you're stuck.
Teach me **Introduction to Monotonic Stack** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Active recall exposes what you missed.
Quiz me on **Introduction to Monotonic Stack** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Intuition + hook + flashcards for long-term memory.
Help me remember **Introduction to Monotonic Stack** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.