Monotonic stack is the interview pattern for nearest-greater, nearest-smaller, and range-boundary problems that would otherwise keep rescanning left or right. It replaces repeated local searches with one disciplined push-pop invariant.
Strong candidates do not describe it as “a stack that stays sorted.” They explain what unresolved indices are waiting for, what condition triggers a pop, and why each index is pushed and popped at most once.
Interview lens
A strong monotonic-stack explanation usually has four parts:
- what each stack entry is still waiting to discover,
- whether the stack must be increasing or decreasing,
- why the current value resolves one or more older indices,
- how the final flush or sentinel handles still-unresolved elements.
Pattern Summary Table
| Pattern | When to use | Key idea | Example problem |
|---|---|---|---|
| Next greater to the right | find the next strictly larger value after each position | keep a decreasing stack of unresolved indices | Daily Temperatures |
| Next smaller to the right | find the next smaller boundary for each position | keep an increasing stack of unresolved indices | Sum of Subarray Minimums |
| Circular next greater | array wraps around and unresolved positions may be answered after the end | simulate two passes while pushing each index once | Next Greater Element II |
| Range boundary contribution | every element contributes across a maximal span | previous/next smaller or greater boundaries determine width | Largest Rectangle in Histogram |
Problem Statement
Given an array, find the nearest greater or smaller element, or compute the maximal range where a value remains dominant.
Typical interview signals:
- brute force keeps scanning left or right from every index
- each index only needs one nearest boundary
- the expected solution is
O(n) - widths, spans, and contribution counts matter more than a full sorted structure
Pattern Recognition Signals
- Keywords in the problem: next greater, next smaller, previous greater, previous smaller, nearest warmer day, span, boundary, rectangle width.
- Structural signals: each position waits for the first future or past value that breaks a comparison rule.
- Complexity signal: nested scans are too slow, but the answer for one index can be finalized permanently once a stronger boundary appears.
Visual Intuition
Store indices in a stack with monotonic order of values.
- decreasing stack -> next greater queries
- increasing stack -> next smaller queries
Each index is pushed and popped at most once.
Optimized Template: Next Greater Element to the Right
What we are doing actually:
- Keep indices in a stack whose values are still waiting for an answer.
- When a bigger value arrives, resolve as many waiting indices as possible.
- Push the current index because it may become the answer for future elements.
public int[] nextGreaterRight(int[] nums) {
int n = nums.length;
int[] ans = new int[n];
Arrays.fill(ans, -1);
Deque<Integer> st = new ArrayDeque<>(); // stores indices
for (int i = 0; i < n; i++) {
while (!st.isEmpty() && nums[i] > nums[st.peek()]) {
ans[st.pop()] = nums[i]; // Current value is the next greater element.
}
st.push(i); // Current index now waits for its own next greater value.
}
return ans;
}
Debug steps:
- print the stack as indices and values after each iteration
- verify each index is pushed once and popped once
- test increasing, decreasing, and equal-value inputs
Problem 1: Daily Temperatures
Problem description: For each day, return how many days you must wait until a warmer temperature appears.
What we are doing actually:
- Keep unresolved day indices in a decreasing stack of temperatures.
- When a warmer day arrives, pop older colder days.
- The distance between indices gives the waiting days.
public int[] dailyTemperatures(int[] t) {
int n = t.length;
int[] ans = new int[n];
Deque<Integer> st = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
while (!st.isEmpty() && t[i] > t[st.peek()]) {
int j = st.pop();
ans[j] = i - j; // Current day is the next warmer day for j.
}
st.push(i); // This day now waits for a warmer future day.
}
return ans;
}
Debug steps:
- print
i,t[i], and the stack before/after pops - verify the stack remains decreasing by temperature
- test strictly decreasing temperatures where all answers stay
0
Problem 2: Next Greater Element II (Circular)
Problem description: Find the next greater element for each position when the array wraps around circularly.
What we are doing actually:
- Simulate two passes over the array using modulo indexing.
- Use the first pass to push indices into the stack.
- Use the second pass only to resolve waiting indices.
public int[] nextGreaterElements(int[] nums) {
int n = nums.length;
int[] ans = new int[n];
Arrays.fill(ans, -1);
Deque<Integer> st = new ArrayDeque<>();
for (int i = 0; i < 2 * n; i++) {
int idx = i % n;
while (!st.isEmpty() && nums[idx] > nums[st.peek()]) {
ans[st.pop()] = nums[idx]; // Wrapped value resolves earlier index.
}
if (i < n) st.push(idx); // Push each index only once.
}
return ans;
}
Debug steps:
- print
i,idx, and current stack during both passes - verify indices are only pushed while
i < n - test arrays where the next greater element appears only after wrapping
Problem 3: Largest Rectangle in Histogram
Problem description: Given bar heights, find the largest rectangle area that can be formed in the histogram.
What we are doing actually:
- Keep indices of increasing heights in the stack.
- When a lower height appears, pop taller bars because their right boundary is now known.
- Compute width using the current index as right boundary and the new stack top as left boundary.
public int largestRectangleArea(int[] h) {
int n = h.length, best = 0;
Deque<Integer> st = new ArrayDeque<>();
for (int i = 0; i <= n; i++) {
int curr = (i == n) ? 0 : h[i]; // Sentinel 0 flushes remaining bars at the end.
while (!st.isEmpty() && curr < h[st.peek()]) {
int height = h[st.pop()];
int left = st.isEmpty() ? -1 : st.peek();
int width = i - left - 1;
best = Math.max(best, height * width); // Max rectangle using popped bar as height.
}
st.push(i); // Current bar may extend future rectangles.
}
return best;
}
Debug steps:
- print popped
height, computedwidth, andbest - verify the sentinel iteration
i == nhappens - test increasing bars, decreasing bars, and a single bar
Common Mistakes
- Storing values instead of indices when distance is required
- Choosing wrong monotonic direction
- Forgetting final flush pass
- Handling duplicates inconsistently (
<vs<=)
Duplicate Tie-Breaking Rule
For contribution-count problems (like subarray minimums), duplicate handling must be asymmetric:
- one side uses strict comparison (
<) - other side uses non-strict (
<=)
Without this, equal values may be double-counted or under-counted.
Document your tie rule before coding.
Debug Template
During development, log stack indices and values:
i=5 val=7 stack=[4(6),2(3)] -> pop 4, answer[4]=7
If results are wrong, inspect:
- whether pops occur at correct comparator condition
- whether unresolved indices are handled in final flush
Most monotonic stack bugs are comparator or flush mistakes.
Practice Problems
- Next Greater Element I (LC 496)
LeetCode - Daily Temperatures (LC 739)
LeetCode - Next Greater Element II (LC 503)
LeetCode - Largest Rectangle in Histogram (LC 84)
LeetCode - Maximal Rectangle (LC 85)
LeetCode - Sum of Subarray Minimums (LC 907)
LeetCode
Key Takeaways
- Monotonic stacks solve neighbor/range-boundary queries in
O(n). - Index storage is usually required for width/distance calculations.
- The push-pop invariant is the core correctness argument.
Categories
Tags
Comments