Skip to main content

๐Ÿ“ˆ Monotonic Stack

Conceptโ€‹

A Monotonic Stack is a stack that maintains elements in either strictly increasing or decreasing order. When a new element violates this property, we pop elements until the invariant is restored.

Every element is pushed and popped at most once, so the total time complexity for processing n elements is O(n).


When to Useโ€‹

  • "Next Greater Element" / "Next Smaller Element"
  • "Previous Greater/Smaller Element"
  • Spans and widths (how far back does the current element dominate?)
  • Histogram area problems
  • Stock span problems

Four Variantsโ€‹

Increasing Stack โ†’ top is SMALLEST โ†’ finds NEXT GREATER to the right
Decreasing Stack โ†’ top is LARGEST โ†’ finds NEXT SMALLER to the right

To find "previous" instead of "next": iterate right to left.


Templateโ€‹

// Next Greater Element (to the right) โ€” use INCREASING stack
public int[] nextGreater(int[] nums) {
int n = nums.length;
int[] result = new int[n];
Arrays.fill(result, -1); // default: no greater element
Deque<Integer> stack = new ArrayDeque<>(); // stores INDICES

for (int i = 0; i < n; i++) {
// While current element is GREATER than stack top โ†’ stack top found its answer
while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) {
result[stack.pop()] = nums[i];
}
stack.push(i);
}
return result;
}

// Previous Smaller Element โ€” use INCREASING stack, iterate Lโ†’R, check BEFORE pushing
public int[] prevSmaller(int[] nums) {
int n = nums.length;
int[] result = new int[n];
Arrays.fill(result, -1);
Deque<Integer> stack = new ArrayDeque<>();

for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && nums[stack.peek()] >= nums[i]) {
stack.pop();
}
result[i] = stack.isEmpty() ? -1 : nums[stack.peek()];
stack.push(i);
}
return result;
}

Worked Example 1: Trapping Rain Waterโ€‹

public int trap(int[] height) {
int n = height.length;
int water = 0;
Deque<Integer> stack = new ArrayDeque<>(); // decreasing stack

for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
int bottom = height[stack.pop()];
if (stack.isEmpty()) break;

int left = stack.peek();
int width = i - left - 1;
int boundedHeight = Math.min(height[left], height[i]) - bottom;
water += width * boundedHeight;
}
stack.push(i);
}
return water;
}

Trace for [0,1,0,2,1,0,1,3,2,1,2,1]:

When we find a "right wall" taller than the stack top,
the stack top is the "bottom" of the water pocket.
The left wall is the new stack top after popping.

Worked Example 2: Sum of Subarray Minimumsโ€‹

Problem: For every subarray, sum up its minimum element.

Key Insight: For each element arr[i], count how many subarrays have arr[i] as their minimum. This = (distance to previous smaller) ร— (distance to next smaller or equal).

public int sumSubarrayMins(int[] arr) {
long MOD = 1_000_000_007L;
int n = arr.length;
int[] left = new int[n]; // distance to previous smaller element
int[] right = new int[n]; // distance to next smaller or equal element

Deque<Integer> stack = new ArrayDeque<>();

// Previous smaller: how many elements to the left (including itself) until smaller
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) stack.pop();
left[i] = stack.isEmpty() ? i + 1 : i - stack.peek();
stack.push(i);
}

stack.clear();

// Next smaller or equal (use >= to avoid double-counting)
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) stack.pop();
right[i] = stack.isEmpty() ? n - i : stack.peek() - i;
stack.push(i);
}

long result = 0;
for (int i = 0; i < n; i++) {
result = (result + (long) arr[i] * left[i] * right[i]) % MOD;
}
return (int) result;
}

Worked Example 3: Daily Temperaturesโ€‹

public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] result = new int[n];
Deque<Integer> stack = new ArrayDeque<>(); // indices of unresolved days

for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
int idx = stack.pop();
result[idx] = i - idx; // days until warmer temperature
}
stack.push(i);
}
return result;
}

LeetCode Problemsโ€‹

๐ŸŸข Easyโ€‹

#ProblemVariant
496Next Greater Element INext greater
682Baseball GameStack basics

๐ŸŸก Mediumโ€‹

#ProblemVariant
402Remove K DigitsMonotonic increasing
503Next Greater Element IICircular + monotonic
739Daily TemperaturesNext greater
856Score of ParenthesesDepth tracking
901Online Stock SpanSpan (prev โ‰ฅ)
907Sum of Subarray MinimumsLeft ร— right spans
1019Next Greater Node in Linked ListNext greater on list

๐Ÿ”ด Hardโ€‹

#ProblemVariant
42Trapping Rain WaterWater between walls
84Largest Rectangle in HistogramWidth span
85Maximal RectangleRow-by-row histogram
1856Maximum Subarray Min-ProductSpan ร— prefix sum