LeetCopilot Logo
LeetCopilot
LeetCode Pattern/Monotonic Stack & Queue/Monotonic Increasing Stack: Template, Next Smaller Element, and Histogram Problems

Monotonic Increasing Stack: Template, Next Smaller Element, and Histogram Problems

LeetCopilot Team
Dec 22, 2025
20 min read
Monotonic StackIncreasing StackNext Smaller ElementHistogramLargest RectangleInterview Prep
Master the monotonic increasing stack pattern for next smaller element and histogram problems. Learn production-ready templates, understand why increasing order works, and solve the canonical largest rectangle problem.

The monotonic increasing stack is the mirror image of the decreasing stack, but it unlocks a completely different class of problems. While decreasing stacks find "next greater" elements, increasing stacks find "next smaller" elements—and this is the key to solving histogram and rectangle problems.

The most famous application? Largest Rectangle in Histogram (LeetCode #84), one of the most elegant O(n) algorithms you'll ever encounter. It seems impossible at first: how can you find the largest rectangle in linear time?

This comprehensive guide will teach you the complete increasing stack template, why it finds next smaller elements, how it solves histogram problems, and the common mistakes that lead to wrong answers.

TL;DR

Use monotonic increasing stack when:

  • Finding next smaller element (to the right or left)
  • Finding previous smaller element
  • Solving histogram or rectangle problems
  • Stack maintains increasing order from bottom to top

Template:

python
stack = []  # Store indices
for i in range(n):
    while stack and nums[i] < nums[stack[-1]]:
        idx = stack.pop()
        # nums[i] is the next smaller for nums[idx]
    stack.append(i)

Time: O(n), Space: O(n)

What Is a Monotonic Increasing Stack?

Definition

A monotonic increasing stack maintains elements in increasing order from bottom to top.

Visual:

code
Bottom → Top
[1, 3, 5, 9]  ✓ Increasing
[9, 5, 3, 1]  ✗ Decreasing
[1, 3, 3, 5]  ✓ Non-strictly increasing

The Invariant

Before pushing a new element, we pop all elements from the top that are greater than or equal to the new element.

This maintains the increasing property.

Example: Building an Increasing Stack

code
Array: [3, 1, 4, 1, 5, 9, 2]

Step 1: Process 3
  Stack: [3]
  
Step 2: Process 1
  1 < 3, pop 3
  Stack: [1]
  
Step 3: Process 4
  4 > 1, just push
  Stack: [1, 4]  (increasing ✓)
  
Step 4: Process 1
  1 < 4, pop 4
  1 = 1, pop 1
  Stack: [1]
  
Step 5: Process 5
  5 > 1, just push
  Stack: [1, 5]  (increasing ✓)
  
Step 6: Process 9
  9 > 5, just push
  Stack: [1, 5, 9]  (increasing ✓)
  
Step 7: Process 2
  2 < 9, pop 9
  2 < 5, pop 5
  2 > 1, just push
  Stack: [1, 2]  (increasing ✓)

Why Increasing Order Finds Next Smaller Element

The Key Insight

When you encounter an element smaller than the stack top, you've found the next smaller element for all larger elements in the stack.

Visual proof:

code
Array: [4, 2, 1, 5, 3]
       [0  1  2  3  4]  ← indices

i=0, val=4: stack=[0]
i=1, val=2: 
  - 2 < 4, so 2 is next smaller for index 0
  - stack=[1]
i=2, val=1:
  - 1 < 2, so 1 is next smaller for index 1
  - stack=[2]
i=3, val=5:
  - 5 > 1, just push
  - stack=[2,3]  (increasing)
i=4, val=3:
  - 3 < 5, so 3 is next smaller for index 3
  - stack=[2,4]

Result:
  result[0] = 2  (next smaller for 4)
  result[1] = 1  (next smaller for 2)
  result[3] = 3  (next smaller for 5)

The Mirror of Decreasing Stack

Comparison:

PatternOrderFindsComparison
Decreasing StackLarge → SmallNext Greaternums[i] > stack[-1]
Increasing StackSmall → LargeNext Smallernums[i] < stack[-1]

Mnemonic: The stack order is the opposite of what you're finding.

The Universal Template

Python Template

python
def next_smaller_element(nums):
    """
    Find next smaller element for each position.
    
    Args:
        nums: List of integers
    
    Returns:
        List where result[i] = next smaller element for nums[i],
        or -1 if no smaller element exists
    
    Time: O(n) - each element pushed and popped at most once
    Space: O(n) - stack storage
    """
    n = len(nums)
    result = [-1] * n  # Default: no smaller element
    stack = []  # Store indices (not values)
    
    for i in range(n):
        # Pop all elements greater than current
        # These elements have found their next smaller (nums[i])
        while stack and nums[i] < nums[stack[-1]]:
            idx = stack.pop()
            result[idx] = nums[i]
        
        # Push current index
        stack.append(i)
    
    # Elements remaining in stack have no next smaller
    # (already initialized to -1)
    
    return result

# Example usage
print(next_smaller_element([4, 2, 1, 5, 3]))
# Output: [2, 1, -1, 3, -1]
# Explanation:
# - Next smaller for 4 is 2
# - Next smaller for 2 is 1
# - No next smaller for 1
# - Next smaller for 5 is 3
# - No next smaller for 3

JavaScript Template

javascript
function nextSmallerElement(nums) {
    const n = nums.length;
    const result = new Array(n).fill(-1);
    const stack = [];
    
    for (let i = 0; i < n; i++) {
        // Pop elements greater than current
        while (stack.length > 0 && nums[i] < nums[stack[stack.length - 1]]) {
            const idx = stack.pop();
            result[idx] = nums[i];
        }
        
        stack.push(i);
    }
    
    return result;
}

// Example
console.log(nextSmallerElement([4, 2, 1, 5, 3]));
// Output: [2, 1, -1, 3, -1]

Java Template

java
public int[] nextSmallerElement(int[] nums) {
    int n = nums.length;
    int[] result = new int[n];
    Arrays.fill(result, -1);
    Stack<Integer> stack = new Stack<>();
    
    for (int i = 0; i < n; i++) {
        // Pop elements greater than current
        while (!stack.isEmpty() && nums[i] < nums[stack.peek()]) {
            int idx = stack.pop();
            result[idx] = nums[i];
        }
        
        stack.push(i);
    }
    
    return result;
}

Pattern 1: Largest Rectangle in Histogram (LeetCode #84)

This is the canonical monotonic increasing stack problem and one of the most beautiful algorithms in computer science.

Problem

Given an array of integers representing histogram bar heights, find the area of the largest rectangle in the histogram.

The Insight

For each bar, the largest rectangle with that bar as the height is determined by:

  • Height: The bar's height
  • Width: Distance between the next smaller bar on the left and next smaller bar on the right

Visual:

code
Heights: [2, 1, 5, 6, 2, 3]
         [0  1  2  3  4  5]

For bar at index 2 (height 5):
- Next smaller to left: index 1 (height 1)
- Next smaller to right: index 4 (height 2)
- Width: 4 - 1 - 1 = 2
- Area: 5 × 2 = 10

Solution

python
def largestRectangleArea(heights):
    """
    LeetCode #84: Largest Rectangle in Histogram
    
    Approach: Monotonic increasing stack
    - For each bar, find next smaller on both sides
    - Calculate area with current bar as height
    
    Time: O(n)
    Space: O(n)
    """
    # Add sentinel values to handle edge cases
    heights = [0] + heights + [0]
    stack = []  # Store indices, maintain increasing heights
    max_area = 0
    
    for i in range(len(heights)):
        # When we find a shorter bar, calculate areas
        while stack and heights[i] < heights[stack[-1]]:
            h_idx = stack.pop()
            height = heights[h_idx]
            
            # Width: distance between next smaller elements
            # Left boundary: stack[-1] (next smaller to left)
            # Right boundary: i (next smaller to right)
            width = i - stack[-1] - 1
            
            area = height * width
            max_area = max(max_area, area)
        
        stack.append(i)
    
    return max_area

# Example
print(largestRectangleArea([2, 1, 5, 6, 2, 3]))
# Output: 10
# Explanation: Rectangle with height 5, width 2 (bars at indices 2 and 3)

Why Sentinels?

Adding [0] at the beginning and end:

  1. Left sentinel (0 at start): Ensures stack is never empty when calculating width
  2. Right sentinel (0 at end): Forces all remaining bars to be processed

Without sentinels, you'd need extra logic to handle edge cases.

Step-by-Step Trace

code
Heights: [0, 2, 1, 5, 6, 2, 3, 0]  (with sentinels)
         [0  1  2  3  4  5  6  7]

i=0, h=0: stack=[0]
i=1, h=2: 2 > 0, stack=[0,1]
i=2, h=1: 
  - 1 < 2, pop 1, height=2, width=2-0-1=1, area=2×1=2
  - 1 > 0, stack=[0,2]
i=3, h=5: 5 > 1, stack=[0,2,3]
i=4, h=6: 6 > 5, stack=[0,2,3,4]
i=5, h=2:
  - 2 < 6, pop 4, height=6, width=5-3-1=1, area=6×1=6
  - 2 < 5, pop 3, height=5, width=5-2-1=2, area=5×2=10 ← MAX
  - 2 > 1, stack=[0,2,5]
i=6, h=3: 3 > 2, stack=[0,2,5,6]
i=7, h=0:
  - 0 < 3, pop 6, height=3, width=7-5-1=1, area=3×1=3
  - 0 < 2, pop 5, height=2, width=7-2-1=4, area=2×4=8
  - 0 < 1, pop 2, height=1, width=7-0-1=6, area=1×6=6
  - stack=[0]

Maximum area: 10

See detailed guide: Histogram Mistakes

Pattern 2: Maximal Rectangle (LeetCode #85)

Problem

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's.

The Insight

Reduce 2D to 1D: Treat each row as the base of a histogram, where the height is the number of consecutive 1's above (including current row).

Visual:

code
Matrix:
[1, 0, 1, 0, 0]
[1, 0, 1, 1, 1]
[1, 1, 1, 1, 1]
[1, 0, 0, 1, 0]

Histograms for each row:
Row 0: [1, 0, 1, 0, 0]
Row 1: [2, 0, 2, 1, 1]
Row 2: [3, 1, 3, 2, 2]
Row 3: [4, 0, 0, 3, 0]

Apply largest rectangle algorithm to each histogram.

Solution

python
def maximalRectangle(matrix):
    """
    LeetCode #85: Maximal Rectangle
    
    Approach: Convert each row to histogram, apply largest rectangle
    
    Time: O(rows × cols)
    Space: O(cols)
    """
    if not matrix or not matrix[0]:
        return 0
    
    cols = len(matrix[0])
    heights = [0] * cols
    max_area = 0
    
    for row in matrix:
        # Update histogram heights
        for i in range(cols):
            if row[i] == '1':
                heights[i] += 1
            else:
                heights[i] = 0
        
        # Find largest rectangle in current histogram
        max_area = max(max_area, largestRectangleArea(heights))
    
    return max_area

# Example
matrix = [
    ["1","0","1","0","0"],
    ["1","0","1","1","1"],
    ["1","1","1","1","1"],
    ["1","0","0","1","0"]
]
print(maximalRectangle(matrix))
# Output: 6
# Explanation: Rectangle from (1,2) to (2,4) has area 2×3=6

Pattern 3: Previous Smaller Element

Finding Previous Smaller (Not Next)

To find the previous smaller element (to the left), we can use the stack differently:

python
def previous_smaller_element(nums):
    """
    Find previous smaller element for each position.
    Stack top is always the previous smaller.
    
    Time: O(n)
    Space: O(n)
    """
    n = len(nums)
    result = [-1] * n
    stack = []  # Maintain increasing order
    
    for i in range(n):
        # Pop elements >= current
        while stack and nums[i] <= nums[stack[-1]]:
            stack.pop()
        
        # Stack top is previous smaller (if exists)
        if stack:
            result[i] = nums[stack[-1]]
        
        stack.append(i)
    
    return result

# Example
print(previous_smaller_element([4, 2, 1, 5, 3]))
# Output: [-1, -1, -1, 1, 1]
# Explanation:
# - 4: no previous smaller
# - 2: no previous smaller
# - 1: no previous smaller
# - 5: previous smaller is 1
# - 3: previous smaller is 1

Bidirectional: Both Left and Right

For histogram problems, we need next smaller on both sides:

python
def find_next_smaller_boundaries(nums):
    """
    Find next smaller element on both left and right.
    Returns (left_boundaries, right_boundaries)
    
    Time: O(n)
    Space: O(n)
    """
    n = len(nums)
    left = [-1] * n   # Next smaller to left
    right = [n] * n   # Next smaller to right
    stack = []
    
    # Find next smaller to right
    for i in range(n):
        while stack and nums[i] < nums[stack[-1]]:
            idx = stack.pop()
            right[idx] = i
        stack.append(i)
    
    # Find next smaller to left
    stack = []
    for i in range(n - 1, -1, -1):
        while stack and nums[i] < nums[stack[-1]]:
            idx = stack.pop()
            left[idx] = i
        stack.append(i)
    
    return left, right

# Example
nums = [2, 1, 5, 6, 2, 3]
left, right = find_next_smaller_boundaries(nums)
print(f"Left: {left}")   # [-1, -1, 1, 2, 1, 4]
print(f"Right: {right}") # [1, 6, 4, 4, 6, 6]

When to Use Increasing Stack

✅ Use Increasing Stack When:

  1. Finding next smaller element

    • Keywords: "next smaller," "next lower," "smaller to the right"
    • Direction: Left to right iteration
  2. Histogram or rectangle problems

    • Keywords: "largest rectangle," "histogram," "maximum area"
    • Example: Largest Rectangle in Histogram, Maximal Rectangle
  3. Finding previous smaller element

    • Keywords: "previous smaller," "smaller to the left"
    • Use stack top after popping larger elements
  4. Range queries with minimum

    • Finding ranges where element is minimum
    • Example: Sum of Subarray Minimums

❌ Don't Use Increasing Stack When:

  1. Finding next greater element

  2. Sliding window problems

  3. Need all elements, not just boundaries

    • Monotonic stack only tracks extremes

Common Mistakes

Mistake 1: Wrong Comparison Direction

Wrong:

python
# Want next SMALLER, but using > instead of <
while stack and nums[i] > nums[stack[-1]]:  # WRONG
    stack.pop()

Correct:

python
# Next SMALLER needs < comparison
while stack and nums[i] < nums[stack[-1]]:  # CORRECT
    idx = stack.pop()
    result[idx] = nums[i]

Mistake 2: Wrong Width Calculation in Histogram

Wrong:

python
width = i - stack[-1]  # Off by one!

Correct:

python
width = i - stack[-1] - 1  # Correct
# Explanation: Exclude both boundaries

Why -1? The boundaries (at stack[-1] and i) are the next smaller elements, so they're not included in the rectangle.

Mistake 3: Not Processing Remaining Stack

Wrong:

python
for i in range(n):
    while stack and nums[i] < nums[stack[-1]]:
        # Process...
    stack.append(i)
# Stack may still have elements!

Correct:

python
# Add sentinel at end to force processing
nums = nums + [0]  # Or handle remaining stack separately

Mistake 4: Forgetting Sentinels in Histogram

Wrong:

python
# Without sentinels, need complex edge case handling
for i in range(len(heights)):
    # ...
    if stack:  # Check if empty
        width = i - stack[-1] - 1
    else:
        width = i  # Special case

Correct:

python
# With sentinels, no special cases needed
heights = [0] + heights + [0]
# Stack is never empty, all bars processed

See guide: Histogram Mistakes

Edge Cases

Edge Case 1: All Same Height

python
heights = [3, 3, 3, 3]
# Largest rectangle: 3 × 4 = 12

Edge Case 2: Strictly Increasing

python
heights = [1, 2, 3, 4, 5]
# Each bar's rectangle is limited by its own height
# Largest: 3 × 3 = 9 (middle bar)

Edge Case 3: Strictly Decreasing

python
heights = [5, 4, 3, 2, 1]
# Widest rectangle: 1 × 5 = 5

Edge Case 4: Single Bar

python
heights = [5]
# Largest rectangle: 5 × 1 = 5

Complexity Analysis

Time Complexity: O(n)

Amortized analysis:

  • Each element is pushed exactly once: n operations
  • Each element is popped at most once: n operations
  • Total: 2n = O(n)

For histogram: O(n) for single pass, even with while loop inside for loop.

Space Complexity: O(n)

Worst case: All elements in increasing order

  • Stack contains all n elements
  • Space: O(n)

See detailed proof: Why Stack Beats Brute Force

Practice Problems

Master the increasing stack with these problems:

Beginner:

  1. Next Smaller Element (practice) - Basic template
  2. Remove K Digits (#402) - Greedy + stack
  3. Remove Duplicate Letters (#316) - Lexicographic order

Intermediate:
4. Largest Rectangle in Histogram (#84) ⭐⭐ - Canonical problem
5. Maximal Rectangle (#85) - 2D extension
6. Sum of Subarray Minimums (#907) - Contribution technique

Advanced:
7. Maximum Width Ramp (#962) - Variant
8. Sum of Subarray Ranges (#2104) - Bidirectional
9. Minimum Cost Tree From Leaf Values (#1130) - DP + stack

FAQ

Q: Why increasing order for next smaller?

A: When we find a smaller element, we know it's the next smaller for all larger elements in the stack. Increasing order ensures we process them correctly.

Q: How do I remember which direction?

A: Mnemonic: The stack order is the opposite of what you're finding.

  • Find greaterdecreasing stack
  • Find smallerincreasing stack

Q: Why are sentinels useful in histogram?

A: Sentinels (0 at both ends) eliminate edge cases:

  • Left sentinel: Stack never empty when calculating width
  • Right sentinel: Forces all bars to be processed

Q: Can I use this for 2D problems?

A: Yes! Maximal Rectangle converts each row to a 1D histogram, then applies the increasing stack algorithm.

Conclusion

The monotonic increasing stack is a powerful pattern for finding next smaller elements and solving histogram problems in O(n) time.

Key principles:

  • Increasing order from bottom to top
  • Pop when current < stack top to find next smaller
  • Histogram: Find next smaller on both sides to calculate width
  • Each element pushed/popped once → O(n)

The template:

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

When to use:

  • "Next smaller element"
  • "Largest rectangle in histogram"
  • "Maximal rectangle"

Master this template, and you'll solve some of the most elegant problems in computer science. For the opposite pattern, see Monotonic Decreasing Stack Template. For the complete overview, see Monotonic Stack & Queue Guide.

Next time you see "histogram" or "largest rectangle," reach for the increasing stack.

Want to Practice LeetCode Smarter?

LeetCopilot is a free browser extension that enhances your LeetCode practice with AI-powered hints, personalized study notes, and realistic mock interviews — all designed to accelerate your coding interview preparation.

Also compatible with Edge, Brave, and Opera

Related Tutorials