Homeβ€Ί DSAβ€Ί Monotonic Stack: Pattern, Use Cases and Coding Examples

Monotonic Stack: Pattern, Use Cases and Coding Examples

Where developers are forged. Β· Structured learning Β· Free forever.
πŸ“ Part of: Stack & Queue β†’ Topic 10 of 10
Master the monotonic stack pattern for Next Greater Element, Stock Span, and Largest Rectangle.
βš™οΈ Intermediate β€” basic DSA knowledge assumed
In this tutorial, you'll learn:
  • Monotonic stack = regular stack + the invariant that elements stay in increasing or decreasing order. Violating elements are popped first.
  • Monotonic decreasing stack β†’ next/previous greater element. Monotonic increasing stack β†’ next/previous smaller element.
  • Time complexity is O(n) β€” each element is pushed at most once and popped at most once regardless of the while loop.
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
⚑ Quick Answer
A monotonic stack is a regular stack with one constraint: elements are always in strictly increasing or decreasing order. When pushing a new element would break that order, you first pop everything that violates it. This simple rule makes it the optimal solution to a whole family of 'next greater/smaller element' problems β€” all in O(n) instead of O(nΒ²).

The monotonic stack is one of the most underrated patterns in algorithm interviews. I've watched engineers spend 45 minutes brute-forcing problems in O(nΒ²) when the monotonic stack gives O(n) in 15 minutes of clean code. Once you internalise the pattern, you'll spot it in problems that don't obviously look like a stack problem β€” and that recognition is what separates strong algorithm interviewers from average ones.

Monotonic Decreasing Stack β€” Next Greater Element

The classic problem: given an array, for each element find the next element to its right that is strictly greater. Brute force is O(nΒ²). Monotonic stack is O(n) β€” each element is pushed and popped at most once.

The invariant: the stack always holds indices of elements in decreasing order of value. When we see a new element larger than the stack top, that top has found its 'next greater' answer.

monotonic_stack.py Β· PYTHON
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
from typing import List

def next_greater_element(nums: List[int]) -> List[int]:
    """
    For each element, find the next strictly greater element to its right.
    Returns -1 if none exists.

    Approach: monotonic decreasing stack of indices.
    When a new element is greater than the stack top, the top has found its answer.

    Time:  O(n) β€” each element pushed/popped at most once
    Space: O(n) β€” stack + result array
    """
    n = len(nums)
    result = [-1] * n
    stack = []          # indices, maintained in decreasing order of nums[index]

    for i in range(n):
        # While current element beats the stack top:
        # the top's 'next greater' is nums[i]
        while stack and nums[i] > nums[stack[-1]]:
            idx = stack.pop()
            result[idx] = nums[i]
        stack.append(i)
    # Remaining indices in stack β†’ no greater element β†’ stay -1

    return result


print(next_greater_element([2, 1, 2, 4, 3]))
# [4, 2, 4, -1, -1]
# Trace:
# i=0: push 0. stack=[0]  (val=2)
# i=1: 1<2 β†’ push 1. stack=[0,1]
# i=2: 2>1 β†’ pop 1, result[1]=2. 2==2 β†’ push 2. stack=[0,2]
# i=3: 4>2 β†’ pop 2, result[2]=4. 4>2 β†’ pop 0, result[0]=4. push 3. stack=[3]
# i=4: 3<4 β†’ push 4. stack=[3,4]
# End: indices 3,4 remain β†’ result[3]=result[4]=-1


def stock_span(prices: List[int]) -> List[int]:
    """
    Stock Span: for each day, how many consecutive previous days
    (including today) had price <= today's price?

    Monotonic decreasing stack of (index, price) pairs.
    Time: O(n), Space: O(n)
    """
    span = [0] * len(prices)
    stack = []   # (index, price) in decreasing price order

    for i, price in enumerate(prices):
        while stack and price >= stack[-1][1]:
            stack.pop()
        span[i] = i + 1 if not stack else i - stack[-1][0]
        stack.append((i, price))

    return span

print(stock_span([100, 80, 60, 70, 60, 75, 85]))
# [1, 1, 1, 2, 1, 4, 6]
β–Ά Output
[4, 2, 4, -1, -1]
[1, 1, 1, 2, 1, 4, 6]

Monotonic Increasing Stack β€” Next Smaller Element and Largest Rectangle

Flip the comparison direction and you get the monotonic increasing stack β€” elements in the stack are always in increasing order. Pop when the current element is smaller than the top. Use for 'next smaller element' problems.

The Largest Rectangle in Histogram is the canonical hard problem that uses this pattern.

monotonic_increasing_stack.py Β· PYTHON
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
from typing import List

def next_smaller_element(nums: List[int]) -> List[int]:
    """
    Monotonic INCREASING stack.
    For each element, find the next strictly smaller element to its right.
    Time: O(n), Space: O(n)
    """
    n = len(nums)
    result = [-1] * n
    stack = []   # indices in increasing order of nums[index]

    for i in range(n):
        while stack and nums[i] < nums[stack[-1]]:
            idx = stack.pop()
            result[idx] = nums[i]
        stack.append(i)

    return result

print(next_smaller_element([4, 2, 3, 1, 5]))
# [2, 1, 1, -1, -1]


def largest_rectangle_histogram(heights: List[int]) -> int:
    """
    Largest rectangle in histogram.
    Classic monotonic increasing stack problem.
    Time: O(n), Space: O(n)

    Key insight: when we pop a bar because the current bar is shorter,
    we know the FULL width that popped bar can extend to.
    """
    max_area = 0
    stack = []   # (start_index, height) β€” monotonic increasing by height
    heights = heights + [0]   # sentinel 0 forces all remaining bars to pop

    for i, h in enumerate(heights):
        start = i
        while stack and stack[-1][1] > h:
            idx, height = stack.pop()
            width    = i - idx
            max_area = max(max_area, height * width)
            start    = idx   # This bar can extend back to where the popped bar started
        stack.append((start, h))

    return max_area

print(largest_rectangle_histogram([2, 1, 5, 6, 2, 3]))
# 10  (bars of height 5 and 6 form a 2-wide Γ— 5-tall = 10 rectangle)
β–Ά Output
[2, 1, 1, -1, -1]
10
Problem TypeStack DirectionPop ConditionClassic Examples
Next Greater Element (right)Decreasingcurrent > topDaily Temperatures, NGE
Previous Greater Element (left)Decreasingcurrent >= topStock Span
Next Smaller Element (right)Increasingcurrent < topNext Smaller Element
Previous Smaller Element (left)Increasingcurrent <= topSum of Subarray Minimums
Area / width problemsIncreasingcurrent < topLargest Rectangle, Trapping Rain Water

🎯 Key Takeaways

  • Monotonic stack = regular stack + the invariant that elements stay in increasing or decreasing order. Violating elements are popped first.
  • Monotonic decreasing stack β†’ next/previous greater element. Monotonic increasing stack β†’ next/previous smaller element.
  • Time complexity is O(n) β€” each element is pushed at most once and popped at most once regardless of the while loop.
  • Store indices in the stack, not values β€” you need the index to compute distances, widths, and spans.
  • When you pop an element, you know its 'answer' β€” the element that triggered the pop is the next greater/smaller for the popped element.

⚠ Common Mistakes to Avoid

  • βœ•Storing values instead of indices in the stack β€” almost every problem requires the index to compute span, width, or distance. Always store indices.
  • βœ•Forgetting the sentinel at the end for problems that require flushing the remaining stack β€” adding a 0 at the end of histogram heights ensures every bar gets processed.
  • βœ•Using strict vs non-strict comparison incorrectly β€” 'pop when current > top' (strict) vs 'pop when current >= top' (non-strict) affects how equal elements are handled. Think through equal-element cases explicitly.

Interview Questions on This Topic

  • QImplement Next Greater Element using a monotonic stack.
  • QWhat is the time complexity of the monotonic stack approach and why?
  • QWhen do you use a monotonic increasing vs decreasing stack?
  • QSolve Largest Rectangle in Histogram in O(n).

Frequently Asked Questions

What is a monotonic stack?

A monotonic stack is a stack where elements are maintained in strictly increasing or decreasing order. When a new element would violate the order, elements are popped until the invariant is restored. This pattern solves 'next/previous greater/smaller element' problems in O(n) time.

When should I use a monotonic stack?

Use it when the problem asks for the next or previous element that is greater or smaller than the current element. Common triggers: 'next greater temperature', 'stock span', 'largest rectangle', 'trapping rainwater'. If a brute-force solution uses a nested loop to look left or right for a boundary, a monotonic stack likely gives O(n).

πŸ”₯
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousStack vs Heap Memory: What Every Developer Must Know
Forged with πŸ”₯ at TheCodeForge.io β€” Where Developers Are Forged