Monotonic StackNext Greater ElementNext Smaller ElementHistogramStock SpanO(n) Optimization

Monotonic Stack — The Complete Guide

Master the Monotonic Stack pattern from first principles. Learn to recognize 'next greater/smaller' problems instantly, evolve brute-force into O(n) solutions, and confidently solve the hardest stack-based interview questions.

40 min read10 sections
01

Intuition from Scratch

Why does this pattern exist?

Imagine you're standing in a line of people with different heights, and you want to know: "For each person, who is the first taller person standing to their right?" The brute-force way is to scan right from every person — that's O(n²). But there's a much smarter way.

Walk through the line from right to left. Keep a stack of people you've seen. For each new person, pop everyone shorter than them off the stack — those shorter people are irrelevant because the current person "blocks" them for anyone further left. The person remaining on top of the stack (if any) is the answer. Then push the current person onto the stack.

The stack always stays in order — either strictly increasing or strictly decreasing from bottom to top. That's why it's called a monotonic stack. The monotonic property is what lets us skip irrelevant elements and solve the problem in O(n) total time, because each element is pushed and popped at most once.

Analogies to Build Intuition

🏙️

Skyscrapers Blocking the View

You're looking at a city skyline from the right. A tall building blocks all shorter buildings behind it. As you scan left, you only care about buildings that are taller than everything you've seen so far. The stack holds the 'visible' buildings — each new tall building pops all shorter ones it blocks.

🌡️

Waiting for a Warmer Day

You check the temperature every day and ask: 'How many days until a warmer day?' You don't need to compare every pair of days. You just need to remember the days that are still 'waiting' for their answer. When a hot day arrives, it resolves all the cooler days stacked up before it.

📚

Stacking Books by Size

You're organizing books on a shelf where each book must be shorter than the one below it (monotonically decreasing). When a tall book arrives, you remove all shorter books on top until you find one that's taller, then place the new book. The removed books have found their 'next taller' — it's the book that just arrived.

What kind of problems does it solve?

Monotonic Stack is your go-to when:

  • You need the next greater element (or next smaller) for each position
  • You need the previous greater element (or previous smaller) for each position
  • You need to compute spans — how far back/forward a condition holds
  • You need to find the largest rectangle in a histogram or matrix
  • You need to compute contribution of each element as min/max of all subarrays
  • You want to reduce an O(n²) "for each element, scan left/right" pattern to O(n)

The core insight

A monotonic stack maintains a sorted order by popping elements that violate the monotonic property. Each pop is meaningful — it pairs the popped element with the element that caused the pop. This pairing is the answer to "next greater/smaller" questions. Since each element is pushed once and popped at most once, the total work is O(n).

02

Pattern Recognition

Recognizing a monotonic stack problem is the hardest part. The problem rarely says "use a stack." Instead, look for these signals.

Keywords & Signals in the Problem Statement

Use Monotonic Stack when you see...

  • "Next greater element" or "next smaller element"
  • "Previous greater/smaller element"
  • "How many days until a warmer temperature"
  • "Stock span" — consecutive days with price ≤ today
  • "Largest rectangle in histogram"
  • "Maximal rectangle in binary matrix"
  • "Sum of subarray minimums/maximums"
  • "Trapping rain water" (can also use two pointers)
  • "Remove K digits to make smallest number"
  • For each element, find the nearest element satisfying a comparison

Don't use Monotonic Stack when...

  • You need the global min/max (just scan once or use a variable)
  • You need the kth largest/smallest (use a heap)
  • The problem involves pairs with a sum/product target (use two pointers or hashing)
  • You need contiguous subarray sum (use sliding window or prefix sum)
  • The comparison involves two different arrays without a clear pairing
  • You need all subarrays explicitly (monotonic stack gives aggregate results)

Monotonic Stack vs. Similar Patterns

PatternWhen to UseKey Difference
Monotonic StackNext/previous greater/smaller, spans, histogram areasMaintains sorted order in stack; each element pushed & popped once → O(n)
Regular StackBracket matching, expression evaluation, undo/redoNo ordering constraint — just LIFO for nesting/pairing
Monotonic DequeSliding window min/max with fixed window sizePops from both ends; maintains candidates within a window
Heap / Priority QueueGlobal kth largest, merge k sorted streamsGives global ordering, not positional next/previous relationships
Two PointersPair sum in sorted data, container problemsConverges from ends; doesn't track 'next greater' relationships

The mental test

Ask yourself: "For each element, do I need to find the nearest element to its left or right that is greater (or smaller)?" If yes, it's a monotonic stack problem. The word "nearest" combined with a comparison is the strongest signal.

03

Brute Force → Optimized Thinking

Let's walk through the thinking evolution using a concrete example: Next Greater Element — for each element in an array, find the first element to its right that is strictly greater. If none exists, the answer is -1.

1

Brute Force — Scan Right for Every Element

O(n²) time · O(1) space

For each element, scan every element to its right until you find one that's greater. This works but is painfully slow for large inputs because of the nested loop.

Brute Forcetypescript
function nextGreaterElement(nums: number[]): number[] {
  const result: number[] = new Array(nums.length).fill(-1);

  for (let i = 0; i < nums.length; i++) {
    for (let j = i + 1; j < nums.length; j++) {
      if (nums[j] > nums[i]) {
        result[i] = nums[j];
        break;  // found the FIRST greater element
      }
    }
  }

  return result;
}
// Input:  [2, 1, 2, 4, 3]
// Output: [4, 2, 4, -1, -1]
// Problem: For each element, we might scan almost the entire array.
2

Key Observation — Elements That Get 'Blocked'

Thinking step

Notice something: if we have [3, 1, 2, 5], when we reach 5, it's the answer for 3, 1, AND 2. All three were 'waiting' for a greater element, and 5 resolves all of them at once. We don't need to re-scan — we just need to remember who's still waiting.

The Insighttext
Array: [3, 1, 2, 5]

Waiting list after processing each element (left to right):
  After 3: waiting = [3]           — 3 has no answer yet
  After 1: waiting = [3, 1]        — 1 < 3, so 1 also waits
  After 2: waiting = [3, 2]        — 2 > 1, so 1's answer is 2. Pop 1.
                                      2 < 3, so 2 waits behind 3.
  After 5: waiting = []            — 5 > 2, so 2's answer is 5. Pop 2.
                                      5 > 3, so 3's answer is 5. Pop 3.

The "waiting list" is a stack that stays in decreasing order!
That's a monotonic decreasing stack.
3

Optimal — Monotonic Stack

O(n) time · O(n) space

Maintain a stack of indices whose next greater element hasn't been found yet. The stack stays in decreasing order of values. When a new element is greater than the stack top, pop and record the answer. Each element is pushed once and popped once → O(n) total.

Monotonic Stack — Optimaltypescript
function nextGreaterElement(nums: number[]): number[] {
  const n = nums.length;
  const result: number[] = new Array(n).fill(-1);
  const stack: number[] = []; // stores indices

  for (let i = 0; i < n; i++) {
    // Pop all elements that are SMALLER than nums[i]
    // For each popped element, nums[i] is their next greater
    while (stack.length > 0 && nums[stack[stack.length - 1]] < nums[i]) {
      const idx = stack.pop()!;
      result[idx] = nums[i];
    }
    stack.push(i);
  }

  // Elements remaining in stack have no next greater → already -1
  return result;
}
// Each element pushed once, popped at most once → O(n) total.

Why Does the Optimization Work?

1

From O(n²) to O(n) — The Amortized Argument

In the brute force, we scan right for every element — potentially O(n) work per element. With the monotonic stack, each element is pushed exactly once and popped at most once. That's at most 2n operations total, regardless of the input. The key: popping resolves the answer immediately, so we never re-scan.

2

Why Decreasing Order?

The stack stays in decreasing order because we only push when the current element is ≤ the stack top. When a larger element arrives, it 'resolves' all smaller elements on top. The decreasing order ensures that the first element to get resolved is the one closest to the current position — exactly the 'next greater' relationship.

3

The General Principle

Monotonic stacks work because they exploit a dominance relationship: a larger element 'dominates' smaller ones to its left. Once dominated, a smaller element will never be the answer for anything further right. So we can safely discard it (pop it) after recording its answer.

The thinking process matters more than the code

In interviews, explain the brute force first, then say: "I notice that when a large element appears, it resolves multiple waiting elements at once. I can use a stack to track elements still waiting for their answer. The stack stays in decreasing order, and each element is processed at most twice — once when pushed, once when popped. That gives me O(n)."

04

Core Templates

Monotonic stack problems have four main templates based on two choices: (1) are you looking for the next or previous element? (2) are you looking for greater or smaller? Memorize these — every problem is a variation.

Template 1: Next Greater Element (Decreasing Stack)

Scan left to right. The stack holds indices of elements waiting for their next greater. Stack values are in decreasing order (bottom to top).

Next Greater Element Templatetypescript
function nextGreater(nums: number[]): number[] {
  const n = nums.length;
  const result = new Array(n).fill(-1);
  const stack: number[] = []; // indices, values in decreasing order

  for (let i = 0; i < n; i++) {
    while (stack.length && nums[stack[stack.length - 1]] < nums[i]) {
      result[stack.pop()!] = nums[i]; // nums[i] is the next greater
    }
    stack.push(i);
  }

  return result;
}
// Stack invariant: values at stack indices are DECREASING
// When nums[i] > stack top → pop (found next greater for that element)

Template 2: Next Smaller Element (Increasing Stack)

Same structure, but the stack holds elements waiting for their next smaller. Stack values are in increasing order. Pop when the current element is smaller.

Next Smaller Element Templatetypescript
function nextSmaller(nums: number[]): number[] {
  const n = nums.length;
  const result = new Array(n).fill(-1);
  const stack: number[] = []; // indices, values in increasing order

  for (let i = 0; i < n; i++) {
    while (stack.length && nums[stack[stack.length - 1]] > nums[i]) {
      result[stack.pop()!] = nums[i]; // nums[i] is the next smaller
    }
    stack.push(i);
  }

  return result;
}
// Stack invariant: values at stack indices are INCREASING
// When nums[i] < stack top → pop (found next smaller for that element)

Template 3: Previous Greater Element (Decreasing Stack)

Scan left to right. For each element, the answer is whatever is currently on top of the stack after popping smaller elements. The stack top is the nearest previous element that is greater.

Previous Greater Element Templatetypescript
function prevGreater(nums: number[]): number[] {
  const n = nums.length;
  const result = new Array(n).fill(-1);
  const stack: number[] = []; // indices, values in decreasing order

  for (let i = 0; i < n; i++) {
    // Pop elements that are NOT greater than nums[i]
    while (stack.length && nums[stack[stack.length - 1]] <= nums[i]) {
      stack.pop();
    }
    // Stack top (if exists) is the previous greater element
    if (stack.length) {
      result[i] = nums[stack[stack.length - 1]];
    }
    stack.push(i);
  }

  return result;
}
// Key difference from "next greater": the ANSWER is read from the stack
// top, not assigned during the pop. The pop just cleans up irrelevant elements.

Template 4: Previous Smaller Element (Increasing Stack)

Scan left to right. Pop elements that are ≥ current. The remaining stack top is the nearest previous smaller element.

Previous Smaller Element Templatetypescript
function prevSmaller(nums: number[]): number[] {
  const n = nums.length;
  const result = new Array(n).fill(-1);
  const stack: number[] = []; // indices, values in increasing order

  for (let i = 0; i < n; i++) {
    while (stack.length && nums[stack[stack.length - 1]] >= nums[i]) {
      stack.pop();
    }
    if (stack.length) {
      result[i] = nums[stack[stack.length - 1]];
    }
    stack.push(i);
  }

  return result;
}
// Stack invariant: values at stack indices are INCREASING
// Stack top after cleanup = nearest previous smaller element

Quick Reference: Which Template?

Looking for...Stack OrderPop ConditionAnswer From
Next GreaterDecreasing ↓stack top < currentPop assigns current as answer
Next SmallerIncreasing ↑stack top > currentPop assigns current as answer
Previous GreaterDecreasing ↓stack top ≤ currentStack top IS the answer
Previous SmallerIncreasing ↑stack top ≥ currentStack top IS the answer

The memory trick

For "next" problems, the answer is discovered during the pop — the current element is the answer for the popped element. For "previous" problems, the answer is read from the stack top — whatever survives the cleanup is the answer for the current element.

05

Variations & Sub-Patterns

The four templates cover the basics, but real interview problems layer additional complexity on top. Here are the most important variations.

VariationTwist on the TemplateClassic Example
Circular ArrayProcess the array twice (2n iterations) to handle wrap-aroundNext Greater Element II (LC 503)
Stock SpanPrevious greater gives the span — distance to the last higher priceOnline Stock Span (LC 901)
Histogram AreaFor each bar, find previous & next smaller to compute width × heightLargest Rectangle in Histogram (LC 84)
Matrix RectangleBuild histogram row by row, apply histogram technique per rowMaximal Rectangle (LC 85)
Subarray Min/Max SumFor each element, find how many subarrays it's the min/max of using prev/next smallerSum of Subarray Minimums (LC 907)
Remove DigitsGreedy + monotonic stack: remove digits that break increasing orderRemove K Digits (LC 402)
Trapping Rain WaterStack tracks bars; when a taller bar arrives, compute trapped water layer by layerTrapping Rain Water (LC 42)

Deep Dive: Largest Rectangle in Histogram

This is the most important monotonic stack problem. For each bar, the largest rectangle using that bar as the shortest has width = (next smaller index) - (previous smaller index) - 1. We find both boundaries using a monotonic increasing stack.

Problems mentioned above

Largest Rectangle in Histogramtypescript
function largestRectangleArea(heights: number[]): number {
  const n = heights.length;
  const stack: number[] = []; // increasing stack of indices
  let maxArea = 0;

  for (let i = 0; i <= n; i++) {
    // Use 0 as a sentinel to flush remaining bars
    const currHeight = i < n ? heights[i] : 0;

    while (stack.length && heights[stack[stack.length - 1]] > currHeight) {
      const height = heights[stack.pop()!];
      // Width: from previous smaller (stack top) to current (next smaller)
      const width = stack.length ? i - stack[stack.length - 1] - 1 : i;
      maxArea = Math.max(maxArea, height * width);
    }

    stack.push(i);
  }

  return maxArea;
}
// For each bar: area = height × (nextSmaller - prevSmaller - 1)
// The stack gives us both boundaries in one pass.
// Sentinel value 0 at the end forces all remaining bars to be processed.

Deep Dive: Sum of Subarray Minimums

For each element, count how many subarrays it's the minimum of. If element at index i has previous-smaller at index left and next-smaller at index right, then it's the minimum of (i - left) × (right - i) subarrays. Multiply by its value and sum everything up.

Sum of Subarray Minimumstypescript
function sumSubarrayMins(arr: number[]): number {
  const MOD = 1e9 + 7;
  const n = arr.length;
  const prevSmaller = new Array(n).fill(-1);  // index of prev smaller
  const nextSmaller = new Array(n).fill(n);   // index of next smaller

  const stack: number[] = [];

  // Previous smaller (strictly less)
  for (let i = 0; i < n; i++) {
    while (stack.length && arr[stack[stack.length - 1]] >= arr[i]) {
      stack.pop();
    }
    prevSmaller[i] = stack.length ? stack[stack.length - 1] : -1;
    stack.push(i);
  }

  stack.length = 0;

  // Next smaller (strictly less — use <= to handle duplicates)
  for (let i = n - 1; i >= 0; i--) {
    while (stack.length && arr[stack[stack.length - 1]] > arr[i]) {
      stack.pop();
    }
    nextSmaller[i] = stack.length ? stack[stack.length - 1] : n;
    stack.push(i);
  }

  let sum = 0;
  for (let i = 0; i < n; i++) {
    const left = i - prevSmaller[i];   // subarrays extending left
    const right = nextSmaller[i] - i;  // subarrays extending right
    sum = (sum + arr[i] * left * right) % MOD;
  }

  return sum;
}
// Key: use strict < for one direction and <= for the other
// to avoid double-counting when there are duplicate values.

Circular arrays

For circular "next greater" problems, iterate through the array twice (indices 0 to 2n-1) and use i % n to wrap around. This ensures every element gets a chance to see elements that come "before" it in the circular order.

06

Visual Walkthrough

Let's trace through two problems step by step to see the monotonic stack in action.

Next Greater Element — Step by Step

Next Greater Element — Tracetext
Input: [4, 2, 1, 5, 3]
Stack stores INDICES. We show values in parentheses for clarity.

i=0, nums[0]=4
  Stack emptypush 0
  Stack: [0(4)]

i=1, nums[1]=2
  2 < 4don't pop → push 1
  Stack: [0(4), 1(2)]

i=2, nums[2]=1
  1 < 2don't pop → push 2
  Stack: [0(4), 1(2), 2(1)]

i=3, nums[3]=5
  5 > 1pop 2result[2] = 5  (1's next greater is 5)
  5 > 2pop 1result[1] = 5  (2's next greater is 5)
  5 > 4pop 0result[0] = 5  (4's next greater is 5)
  Stack emptypush 3
  Stack: [3(5)]

i=4, nums[4]=3
  3 < 5don't pop → push 4
  Stack: [3(5), 4(3)]

Done. Remaining in stackno next greater → -1.

Result: [5, 5, 5, -1, -1]

Key observation: When 5 arrived, it resolved THREE elements at once.
That's the power of the monotonic stack — batch resolution.

Largest Rectangle in Histogram — Step by Step

Histogram — Tracetext
Heights: [2, 1, 5, 6, 2, 3]
Append sentinel 0: [2, 1, 5, 6, 2, 3, 0]
Stack stores indices. Increasing stack (by height).

i=0, h=2: Stack emptypush 0.  Stack: [0]
i=1, h=1: 1 < 2pop 0.
           height=2, width=1 (no stack left, so width=i=1)
           area = 2×1 = 2. maxArea=2
           push 1.  Stack: [1]

i=2, h=5: 5 > 1push 2.  Stack: [1, 2]
i=3, h=6: 6 > 5push 3.  Stack: [1, 2, 3]

i=4, h=2: 2 < 6pop 3.
           height=6, width = 4-2-1 = 1. area=6. maxArea=6
           2 < 5pop 2.
           height=5, width = 4-1-1 = 2. area=10. maxArea=10
           21stop. push 4.  Stack: [1, 4]

i=5, h=3: 3 > 2push 5.  Stack: [1, 4, 5]

i=6, h=0 (sentinel): flushes everything.
           pop 5: height=3, width=6-4-1=1. area=3.
           pop 4: height=2, width=6-1-1=4. area=8.
           pop 1: height=1, width=6 (stack empty). area=6.

Final maxArea = 10 (bar of height 5, spanning indices 2-3)

The sentinel (height 0) guarantees all bars get processed.
Width formula: stack emptywidth = i. Otherwisei - stack.top - 1.

Daily Temperatures — Step by Step

Daily Temperatures — Tracetext
Temps: [73, 74, 75, 71, 69, 72, 76, 73]
Stack stores indices. Decreasing stack (by temperature).

i=0, t=73: push 0.  Stack: [0(73)]
i=1, t=74: 74>73pop 0result[0]=1-0=1. push 1.  Stack: [1(74)]
i=2, t=75: 75>74pop 1result[1]=2-1=1. push 2.  Stack: [2(75)]
i=3, t=71: 71<75push 3.  Stack: [2(75), 3(71)]
i=4, t=69: 69<71push 4.  Stack: [2(75), 3(71), 4(69)]
i=5, t=72: 72>69pop 4result[4]=5-4=1.
           72>71pop 3result[3]=5-3=2.
           72<75stop. push 5.  Stack: [2(75), 5(72)]
i=6, t=76: 76>72pop 5result[5]=6-5=1.
           76>75pop 2result[2]=6-2=4.
           push 6.  Stack: [6(76)]
i=7, t=73: 73<76push 7.  Stack: [6(76), 7(73)]

Remainingresult stays 0 (no warmer day).

Result: [1, 1, 4, 2, 1, 1, 0, 0]

Notice: result[i] = j - i (distance), not the temperature itself.
This is a "next greater" problem where the answer is the INDEX DISTANCE.
07

Practice Problems

These 7 problems are carefully ordered from easy to hard. Each one teaches a different aspect of the monotonic stack. Solve them in order.

1

LC 496. Next Greater Element I

Easy

Why this pattern:

The purest monotonic stack problem. Given two arrays, find the next greater element for each element of nums1 in nums2. Teaches the basic decreasing stack template with a hash map for lookups.

Key idea: Build a next-greater map for all elements in nums2 using a monotonic decreasing stack. Then look up each element of nums1 in the map. The stack processes nums2 in O(n), and each lookup is O(1).

2

LC 739. Daily Temperatures

Medium

Why this pattern:

Next greater element where the answer is the INDEX DISTANCE, not the value. The stack stores indices, and when you pop, the answer is current_index - popped_index.

Key idea: Maintain a decreasing stack of indices. When temperature[i] > temperature[stack.top], pop and set result[popped] = i - popped. This is the 'how many days until warmer' question — a direct application of next greater.

3

LC 901. Online Stock Span

Medium

Why this pattern:

Previous greater element in disguise. The span is the number of consecutive days (including today) where the price was ≤ today's price. That's the distance to the previous greater price.

Key idea: Maintain a decreasing stack of (price, index) pairs. For each new price, pop all entries with price ≤ current. The span = current_index - stack.top.index (or current_index + 1 if stack is empty). This is a streaming/online variant — you process one element at a time.

4

LC 503. Next Greater Element II

Medium

Why this pattern:

Circular array variant. The array wraps around, so the next greater element for the last element might be at the beginning. Solved by iterating through the array twice (2n iterations).

Key idea: Loop from i = 0 to 2n-1, using i % n for the actual index. Only push during the first pass (i < n). Pop as usual — the circular iteration ensures every element sees all possible candidates.

5

LC 907. Sum of Subarray Minimums

Medium

Why this pattern:

Contribution technique: for each element, count how many subarrays it's the minimum of. Uses both previous-smaller and next-smaller boundaries. Requires careful handling of duplicates.

Key idea: For element at index i: left = i - prevSmaller[i], right = nextSmaller[i] - i. Contribution = arr[i] × left × right. Use strict < for one direction and ≤ for the other to avoid double-counting duplicates.

6

LC 84. Largest Rectangle in Histogram

Hard

Why this pattern:

The crown jewel of monotonic stack problems. For each bar, find the widest rectangle where that bar is the shortest. Width = nextSmaller - prevSmaller - 1. Uses an increasing stack with a sentinel.

Key idea: Maintain an increasing stack. When a shorter bar arrives, pop and compute area = popped_height × width. Width = i - stack.top - 1 (or i if stack is empty). Append a sentinel bar of height 0 to flush all remaining bars.

7

LC 85. Maximal Rectangle

Hard

Why this pattern:

Builds on the histogram problem. Treat each row of the binary matrix as the base of a histogram — heights[j] = number of consecutive 1s above (including current row). Apply LC 84 to each row's histogram.

Key idea: For each row, update heights: if matrix[i][j] = '1', heights[j]++; else heights[j] = 0. Then run largestRectangleArea(heights) for that row. The answer is the maximum across all rows.

Practice strategy

For each problem: (1) Identify whether you need next/previous and greater/smaller. (2) Decide if the stack should be increasing or decreasing. (3) Determine whether the answer comes from the pop or the stack top. (4) After solving, write: "This is [next/prev] [greater/smaller] because [signal]."

08

Common Mistakes

These are the traps that catch people on monotonic stack problems. Learn them here so you don't learn them in an interview.

🔀

Using the wrong stack order (increasing vs decreasing)

You use a decreasing stack when you need an increasing one, or vice versa. The results are completely wrong and hard to debug because the code 'looks right.'

Remember: for 'next GREATER', you need a DECREASING stack (pop smaller elements). For 'next SMALLER', you need an INCREASING stack (pop larger elements). The stack order is the OPPOSITE of what you're looking for.

📊

Storing values instead of indices in the stack

You push values onto the stack but then can't compute distances (like 'how many days until warmer') because you don't know the positions.

Almost always store INDICES in the stack. You can always look up the value with nums[stack[stack.length - 1]]. Indices give you both the value AND the position.

🔁

Forgetting the sentinel in histogram problems

After processing all bars, some bars remain in the stack unprocessed. You miss rectangles that extend to the right edge of the histogram.

Append a sentinel bar of height 0 at the end. This forces every remaining bar to be popped and processed. Alternatively, add a cleanup loop after the main loop.

🔢

Double-counting with duplicate values

In 'Sum of Subarray Minimums', duplicate values cause the same subarray to be counted twice because both copies claim to be the minimum.

Use strict inequality (<) for one direction and non-strict (≤) for the other. This breaks ties consistently: one copy 'owns' subarrays to the left, the other 'owns' subarrays to the right.

↔️

Confusing 'next' vs 'previous' template logic

For 'next' problems, the answer is assigned during the pop. For 'previous' problems, the answer is read from the stack top. Mixing these up gives wrong results.

'Next': when you pop element X because of element Y, Y is X's next greater/smaller. 'Previous': after popping, whatever is left on top is the current element's previous greater/smaller.

📐

Wrong width calculation in histogram

You compute width as i - popped_index, but the correct width is i - stack.top - 1 (or i if the stack is empty). Off-by-one errors change the answer significantly.

The width extends from (previous smaller + 1) to (next smaller - 1). Previous smaller is the new stack top after popping. So width = i - stack.top - 1. If stack is empty, the bar extends all the way to the left: width = i.

09

Interview Strategy

Monotonic stack problems are a favorite at FAANG companies because they test whether you can see through a problem to its underlying structure. Here's how to present your thinking.

The 5-Step Interview Flow

1

Identify the Core Question

Reframe the problem: 'For each element, I need to find the nearest [greater/smaller] element to its [left/right].' If you can phrase it this way, you know it's a monotonic stack problem. Say this out loud to the interviewer.

2

State the Brute Force

'The brute force is O(n²) — for each element, scan left/right to find the answer. But I notice that elements get 'resolved' in batches when a dominant element arrives. I can use a monotonic stack to track unresolved elements.'

3

Explain the Stack Invariant

'I'll maintain a stack in [decreasing/increasing] order. When a new element violates the order, I pop — and the pop itself gives me the answer. Each element is pushed once and popped once, so the total time is O(n).'

4

Code It Clean

Use clear variable names. Store indices, not values. Add a comment for the stack invariant. For histogram problems, mention the sentinel. Keep the while-pop loop tight and readable.

5

Test & Analyze

Trace through a small example showing pushes and pops. Verify edge cases: all increasing, all decreasing, all equal, single element. State complexity: O(n) time (each element pushed/popped once), O(n) space for the stack.

Common Follow-Up Questions

Follow-UpHow to Handle
"What if the array is circular?"Iterate 2n times, use i % n for indexing. Only push during the first n iterations. This handles wrap-around naturally.
"Can you do it in O(1) space?"For some problems (like Daily Temperatures), you can iterate right-to-left and use the result array itself to skip ahead. But the standard O(n) stack solution is usually expected and accepted.
"What about duplicates?"Use strict < for one direction and ≤ for the other to break ties consistently. Explain why: it prevents double-counting by assigning ownership of boundary subarrays to exactly one copy.
"Extend to 2D (matrix)?"Build a histogram per row (heights of consecutive 1s), then apply the 1D histogram technique to each row. This is how Maximal Rectangle (LC 85) works.
"What if elements are streaming (online)?"The stack naturally supports online processing — you push each new element and pop as needed. Stock Span (LC 901) is exactly this: process one element at a time, maintain the stack across calls.
"Why not use a priority queue instead?"A heap gives you the global min/max, but not the NEAREST greater/smaller by position. The monotonic stack preserves positional ordering, which is what 'next/previous' problems need.

The meta-skill

The hardest part of monotonic stack problems isn't the code — it's recognizing that the problem reduces to "next/previous greater/smaller." Problems like Largest Rectangle in Histogram and Trapping Rain Water don't mention stacks at all. Practice reframing problems in terms of nearest dominant elements — that's the real interview skill.

10

Summary & Cheat Sheet

Pin this section. Review it before mock interviews and contests.

Quick Revision Cheat Sheet

Core idea: A stack that maintains sorted order (increasing or decreasing). Pops pair elements with their nearest dominant neighbor → O(n)

Why O(n)?: Each element is pushed once and popped at most once. Total operations ≤ 2n regardless of input

Next greater: Decreasing stack. Pop when current > top. Popped element's answer = current element

Next smaller: Increasing stack. Pop when current < top. Popped element's answer = current element

Previous greater: Decreasing stack. After popping, stack top IS the answer for current element

Previous smaller: Increasing stack. After popping, stack top IS the answer for current element

Store indices: Almost always push indices, not values. Indices give you position AND value via nums[i]

Histogram trick: For each bar: area = height × (nextSmaller - prevSmaller - 1). Use sentinel height 0 to flush

Circular arrays: Iterate 2n times with i % n. Only push during first n. Handles wrap-around

Duplicate handling: Use < for one direction, ≤ for the other. Prevents double-counting

Contribution technique: Element i is min of (i - prevSmaller) × (nextSmaller - i) subarrays

Mental trigger: "Nearest" + "greater/smaller" + "left/right" → monotonic stack. Also: histogram, span, temperatures

Decision Flowchart

Which Monotonic Stack Variant? — Decision Treetext
What does the problem ask?
├── "Next greater element" for each position?
│   → Decreasing stack, scan leftright, answer on POP
├── "Next smaller element" for each position?
│   → Increasing stack, scan leftright, answer on POP
├── "Previous greater element" for each position?
│   → Decreasing stack, scan leftright, answer = STACK TOP
├── "Previous smaller element" for each position?
│   → Increasing stack, scan leftright, answer = STACK TOP
├── "Largest rectangle in histogram"?
│   → Increasing stack + sentinel. Area = height × width on pop
├── "Sum of subarray mins/maxs"?
│   → Find prev & next smaller/greater. Contribution = val × left × right
├── "Stock span" or "days until warmer"?
│   → Previous/next greater. Answer = index distance
├── Circular array?
│   → Same template but iterate 2n times with i % n
└── Not sure?
Ask: "Can I rephrase this as 'nearest greater/smaller'?"
    ├── YESPick the right template above
    └── NOProbably not a monotonic stack problem

One last thing

Monotonic stack is one of the highest-ROI patterns for interviews. It appears in easy problems (Next Greater Element I) and hard ones (Largest Rectangle, Maximal Rectangle). The technique is always the same — maintain a sorted stack, pop when violated, and the pop gives you the answer. Master the 4 templates in Section 04, solve the 7 problems in Section 07, and you'll recognize this pattern instantly in any interview.