Stacks and Queues · Stacks and Queues Fundamentals

Monotonic Stack Technique

Keeping a stack whose elements stay in strictly increasing or decreasing order by popping anything that violates it before pushing - so each element is pushed and popped at most once, no matter how the "look right/left for the next bigger element" comparisons play out.

  • 6 min read
  • 10 practice questions
  • 5 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: count what each element is the minimum of
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

For every element, find the next element to its right that is larger. Checking everything to the right of each element is O(n^2). But an element that has been beaten by a larger one to its right can never be the answer for anything further left - so it can be thrown away, and the survivors form a stack in sorted order.

This skill covers the monotonic stack: one pass that keeps only the candidates that can still be an answer, giving next greater or smaller elements in O(n). It also covers the contribution trick built on it, where each element's span as the minimum turns sums over all subarrays into one pass.

We'll go from simple to more technical:

  1. The pattern: next greater element, popping everything the current element beats.
  2. A second flavor: previous and next smaller spans, and each element's contribution.
  3. Where it shows up (days until warmer, stock span, window statistics).
  4. What breaks it: the duplicate rule, double-counted ties, sentinels, the O(n^2) illusion.

The approach

Java

int[] nextGreaterElement(int[] nums) {
    int n = nums.length;
    int[] result = new int[n];
    Deque<Integer> stack = new ArrayDeque<>();        // candidates, increasing from top to bottom
    for (int i = n - 1; i >= 0; i--) {
        while (!stack.isEmpty() && stack.peek() <= nums[i]) {
            stack.pop();                              // not greater than nums[i]: useless for everything further left
        }
        result[i] = stack.isEmpty() ? -1 : stack.peek();
        stack.push(nums[i]);
    }
    return result;
}

Python

def next_greater_element(nums: list[int]) -> list[int]:
    n = len(nums)
    result = [-1] * n
    stack = []                                     # candidates, increasing from top to bottom
    for i in range(n - 1, -1, -1):
        while stack and stack[-1] <= nums[i]:
            stack.pop()                            # not greater than nums[i]: useless for everything further left
        result[i] = stack[-1] if stack else -1
        stack.append(nums[i])
    return result

Scanning right to left, the stack holds the candidates for "next greater" in increasing order from top to bottom. Anything not greater than nums[i] is popped: nums[i] is closer and at least as large, so it will block it for every earlier element. Each element is pushed once and popped at most once - O(n) in total, though the loops look nested. The pop condition decides duplicates: <= makes "greater" strict (the next greater of the first 2 in [2, 2, 3] is 3); popping only < would wrongly report 2.

Two common variants follow the same shape. Next smaller: flip the comparison. Stock span (how many consecutive days up to today had a price <= today's) scans left to right and keeps (price, span) pairs: pop every pair with price <= today's and add its span to today's.

A second flavor: count what each element is the minimum of

The previous and next smaller elements tell you the whole stretch where an element is the minimum. That turns several problems into "for each element, add up its contribution":

Java

long sumSubarrayMins(int[] arr) {
    int n = arr.length;
    int[] left = new int[n], right = new int[n];
    Deque<Integer> stack = new ArrayDeque<>();
    for (int i = 0; i < n; i++) {                     // previous STRICTLY smaller
        while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) stack.pop();
        left[i] = stack.isEmpty() ? -1 : stack.peek();
        stack.push(i);
    }
    stack.clear();
    for (int i = n - 1; i >= 0; i--) {                // next smaller OR EQUAL
        while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) stack.pop();
        right[i] = stack.isEmpty() ? n : stack.peek();
        stack.push(i);
    }
    long sum = 0;
    for (int i = 0; i < n; i++) sum += (long) arr[i] * (i - left[i]) * (right[i] - i);
    return sum;
}

Python

def sum_subarray_mins(arr: list[int]) -> int:
    n = len(arr)
    left, right = [-1] * n, [n] * n
    stack = []
    for i in range(n):                             # previous STRICTLY smaller
        while stack and arr[stack[-1]] >= arr[i]:
            stack.pop()
        left[i] = stack[-1] if stack else -1
        stack.append(i)
    stack.clear()
    for i in range(n - 1, -1, -1):                 # next smaller OR EQUAL
        while stack and arr[stack[-1]] > arr[i]:
            stack.pop()
        right[i] = stack[-1] if stack else n
        stack.append(i)
    return sum(arr[i] * (i - left[i]) * (right[i] - i) for i in range(n))   # Python ints: no overflow

arr[i] is the minimum of every subarray that starts after left[i] and ends before right[i]: (i - left[i]) x (right[i] - i) of them. With equal values, one side must be strict and the other not, so a subarray whose minimum appears twice is credited to exactly one of them - with both sides strict, [1, 1] sums to 4 instead of 3. The same spans answer "maximum of minimums for every window size": element i is the minimum of a window of length right[i] - left[i] - 1, and a best value for a longer window is also achievable for every shorter one.

Where you'll see it

"How many days until a warmer day", "the next price higher than today's" and trading indicators like the stock span are next-greater problems over time series. Contribution counting appears in analytics that sum a statistic over every time window without enumerating the windows.

Which tool for which job?

You needUse
Next greater or smaller for each elementa monotonic stack, one pass
How far back the run of smaller values goes (stock span)a stack of (value, span) pairs
A sum of minimums or maximums over all subarraysspans from both sides, then value x left x right
The sum of all subarray rangessum of maxima minus sum of minima
The smallest number after removing k digitsa greedy increasing stack
Next greater in a circular arrayscan 2n positions with i % n

Common mistakes

MistakeWhat happensDo this instead
< where <= is needed[2, 2, 3] reports 2 as the next greaterpop equal values when "greater" is strict
Both span sides strict[1, 1] sums to 4 instead of 3one side strict, the other not
-1 as "none" when -1 can be an answerambiguous resultsa sentinel outside the value range
Contributions summed in int30,000 copies of 30,000 wrappedaccumulate in long
Assuming it's O(n^2)nested loops look quadraticeach element is pushed and popped once

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why is the next-greater scan O(n) despite the inner while loop?

Show answer

Every element is pushed exactly once and popped at most once over the whole scan, so the inner loop runs at most n times in total, not n times per element.

2. For [2, 2, 3], what is the next greater element of the first 2, and which pop condition gives it?

Show answer
  1. Popping values <= nums[i] removes the equal 2, leaving 3 on the stack. Popping only < leaves the 2 there and reports it - but 2 isn't strictly greater than 2.

3. In sum of subarray minimums, why is one boundary strict and the other not?

Show answer

A subarray whose minimum value appears several times must be credited to exactly one of those positions. Using "previous strictly smaller" on one side and "next smaller or equal" on the other assigns it to the leftmost minimum; with both sides strict, [1, 1] counts the whole array twice (4 instead of 3).

Key takeaways

  • Keep a monotonic stack; pop what the current element beats; the top is the answer. O(n).
  • <= versus < in the pop decides how duplicates are treated.
  • Previous and next smaller give each element's span as the minimum: sum x span counts, windows and rectangles.
  • Make one side strict and the other non-strict to avoid counting ties twice.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Next greater or smallermonotonic stack
Stock span(value, span) stack
Sum of subarray mins or maxesspans x value
Sum of rangesmaxes - mins
Remove k digitsincreasing stack, greedy
Circular2n with i % n

Snippets worth knowing by heart

Java

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

Python

st = []                                  # remove k digits
for d in num:
    while k and st and st[-1] > d:
        st.pop()
        k -= 1
    st.append(d)
result = "".join(st[:len(st) - k] if k else st).lstrip("0") or "0"

The traps

  • < versus <= with duplicates: [2, 2, 3] reports 2 as the next greater. Pop equal values when "greater" is strict.
  • Ties counted twice: with both span sides strict, [1, 1] sums to 4 instead of 3. Make one side strict, one not.
  • Colliding sentinels: -1 as "none" is ambiguous when -1 can be an answer. Pick a sentinel outside the value range.
  • int contribution sums: large inputs wrap. Accumulate in long.

Staff-level points, one line each

  • 100,000 values: at most n pops in total; the stack peaked at 25 on random data and 100,000 when nothing pops.
  • Sum of ranges = sum of maxima - sum of minima; it matched brute force on 300 arrays with ties.
  • Remove k digits: "1432219", k = 3 gives 1219; it matched brute force on 300 cases.
  • Visibility: the stack is the skyline seen from the current position.
  • 30,000 copies of 30,000: the true sum 13,500,450,000,000 came out as 1367788672 in an int.

Say it in an interview

  • "Anything the current element beats can never be the answer further left, so I pop it."
  • "Each index is pushed and popped once, so it's O(n)."
  • "For sums over all subarrays I count each element's span."
  • "One side strict, one side not, so ties count once."

Practice

This skill comes with 10 multiple-choice questions that tell you which misconception a wrong answer reveals and 5 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Monotonic Stack Technique free

Next Greater Element I

MediumProCoding problemJava · Python

Given an array nums, for each element return the next element to its right that's strictly greater than it, or -1 if none exists.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Online Stock Span

MediumProCoding problemJava · Python

For each day's price, report its span: how many days in a row, counting back from today and including today, the price was at or below today's price. Each answer may only use the prices up to that day.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Next Smaller Element

MediumProCoding problemJava · Python

Given an array nums, for each element return the next element to its right that's strictly smaller than it, or -1 if none exists.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Maximum of Minimums for Every Window Size

HardProCoding problemJava · Python

Given an array nums of size n, for every window size k from 1 to n, find the maximum value of "the minimum element in a window of size k", over all windows of that size. Return the results indexed so result[k-1] is the answer for window size k.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Sum of Subarray Minimums

HardProCoding problemJava · Python

Take every run of one or more consecutive elements of arr, find the smallest value in each run, and return the total of those minimums.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Monotonic Stack Technique free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime