LeetCopilot Logo
LeetCopilot
LeetCode Pattern/Monotonic Stack & Queue/Debugging Monotonic Stack Solutions: A Systematic Approach

Debugging Monotonic Stack Solutions: A Systematic Approach

LeetCopilot Team
Dec 22, 2025
11 min read
Monotonic StackDebuggingTroubleshootingProblem SolvingInterview Prep
Learn a systematic debugging process for monotonic stack problems. Trace stack state, identify common bugs, verify invariants, and fix solutions quickly with this step-by-step guide.

Your monotonic stack solution isn't working. You've stared at the code for 20 minutes. The bug is invisible.

This is the debugging nightmare every developer faces with monotonic stacks. The logic seems right, but the output is wrong. Where's the bug?

This guide will teach you a systematic debugging approach that works every time: how to trace stack state, identify common bug patterns, verify invariants, and fix solutions in under 5 minutes.

TL;DR

The systematic debugging process:

  1. Verify monotonicity direction (increasing vs decreasing)
  2. Check popping condition (< vs > vs <= vs >=)
  3. Trace stack state (print at each iteration)
  4. Verify result initialization (pre-allocated with defaults)
  5. Check boundary conditions (empty array, single element)

Quick debug template:

python
for i in range(n):
    print(f"i={i}, val={nums[i]}, stack={stack}")  # Debug print
    while stack and condition:
        # ...
    print(f"  After: stack={stack}, result={result}")  # Debug print

The Debugging Framework

Step 1: Verify Monotonicity Direction

Question: Should the stack be increasing or decreasing?

Rule:

  • Next GREATERDecreasing stack
  • Next SMALLERIncreasing stack

Check:

python
# For next greater element:
# Stack should be decreasing (large at bottom, small at top)

# Example: After processing [5, 3, 1]
# Correct: stack = [5, 3, 1]  (decreasing ✓)
# Wrong:   stack = [1, 3, 5]  (increasing ✗)

How to verify:

python
# Add assertion after each push
stack.append(i)
# Verify decreasing order (for next greater)
if len(stack) >= 2:
    assert nums[stack[-2]] > nums[stack[-1]], "Stack not decreasing!"

Step 2: Check Popping Condition

Common bug: Using wrong comparison operator

Checklist:

code
Next GREATER element:
  ✓ while stack and nums[i] > nums[stack[-1]]
  ✗ while stack and nums[i] >= nums[stack[-1]]  (too aggressive)
  ✗ while stack and nums[i] < nums[stack[-1]]   (wrong direction)

Next SMALLER element:
  ✓ while stack and nums[i] < nums[stack[-1]]
  ✗ while stack and nums[i] <= nums[stack[-1]]  (too aggressive)
  ✗ while stack and nums[i] > nums[stack[-1]]   (wrong direction)

Debug technique:

python
while stack and nums[i] > nums[stack[-1]]:
    print(f"  Popping {stack[-1]} (val={nums[stack[-1]]}) because {nums[i]} > {nums[stack[-1]]}")
    idx = stack.pop()
    result[idx] = nums[i]

Step 3: Trace Stack State

The most powerful debugging technique: Print stack state at each iteration.

Template:

python
def nextGreaterElement_debug(nums):
    n = len(nums)
    result = [-1] * n
    stack = []
    
    print("=== Starting ===")
    for i in range(n):
        print(f"\ni={i}, val={nums[i]}")
        print(f"  Before: stack={stack}")
        
        while stack and nums[i] > nums[stack[-1]]:
            idx = stack.pop()
            result[idx] = nums[i]
            print(f"    Popped {idx}, set result[{idx}]={nums[i]}")
        
        stack.append(i)
        print(f"  After: stack={stack}")
        print(f"  Result so far: {result}")
    
    print(f"\nFinal result: {result}")
    return result

# Test
nextGreaterElement_debug([2, 1, 2, 4, 3])

Output:

code
=== Starting ===

i=0, val=2
  Before: stack=[]
  After: stack=[0]
  Result so far: [-1, -1, -1, -1, -1]

i=1, val=1
  Before: stack=[0]
  After: stack=[0, 1]
  Result so far: [-1, -1, -1, -1, -1]

i=2, val=2
  Before: stack=[0, 1]
    Popped 1, set result[1]=2
  After: stack=[0, 2]
  Result so far: [-1, 2, -1, -1, -1]

i=3, val=4
  Before: stack=[0, 2]
    Popped 2, set result[2]=4
    Popped 0, set result[0]=4
  After: stack=[3]
  Result so far: [4, 2, 4, -1, -1]

i=4, val=3
  Before: stack=[3]
  After: stack=[3, 4]
  Result so far: [4, 2, 4, -1, -1]

Final result: [4, 2, 4, -1, -1]

Step 4: Verify Result Initialization

Common bug: Not pre-initializing result array

Check:

python
# ✗ Wrong
result = []  # Empty list causes IndexError

# ✓ Correct
result = [-1] * n  # Pre-allocated with default

Debug technique:

python
# Verify size
assert len(result) == len(nums), f"Result size mismatch: {len(result)} vs {len(nums)}"

# Verify all initialized
assert all(r == -1 for r in result), "Result not initialized to -1"

Step 5: Check Boundary Conditions

Test these edge cases:

python
# Edge case 1: Empty array
assert nextGreaterElement([]) == []

# Edge case 2: Single element
assert nextGreaterElement([5]) == [-1]

# Edge case 3: Two elements
assert nextGreaterElement([1, 2]) == [2, -1]
assert nextGreaterElement([2, 1]) == [-1, -1]

# Edge case 4: All same
assert nextGreaterElement([3, 3, 3]) == [-1, -1, -1]

# Edge case 5: Strictly increasing
assert nextGreaterElement([1, 2, 3, 4]) == [2, 3, 4, -1]

# Edge case 6: Strictly decreasing
assert nextGreaterElement([4, 3, 2, 1]) == [-1, -1, -1, -1]

Common Bug Patterns

Bug Pattern 1: Off-by-One in Index

Symptom: IndexError or wrong results

Example:

python
# ✗ Wrong
for i in range(len(nums) - 1):  # Misses last element!
    # ...

# ✓ Correct
for i in range(len(nums)):  # Includes all elements
    # ...

Bug Pattern 2: Wrong Stack Content

Symptom: Stack contains values instead of indices (or vice versa)

Example:

python
# ✗ Wrong: Storing values
stack.append(nums[i])  # Can't calculate distance later!

# ✓ Correct: Storing indices
stack.append(i)  # Can access nums[stack[-1]] and calculate i - stack[-1]

Debug check:

python
# Verify stack contains indices
if stack:
    assert all(0 <= idx < len(nums) for idx in stack), "Stack contains invalid indices"

Bug Pattern 3: Not Handling Duplicates

Symptom: Wrong results on arrays with duplicate values

Example:

python
# For contribution technique:
# ✗ Wrong: Symmetric comparison
while stack and arr[i] < arr[stack[-1]]:  # Right
while stack and arr[i] < arr[stack[-1]]:  # Left (same!)

# ✓ Correct: Asymmetric comparison
while stack and arr[i] < arr[stack[-1]]:   # Right: strict
while stack and arr[i] <= arr[stack[-1]]:  # Left: non-strict

Bug Pattern 4: Circular Array Single Pass

Symptom: Wrong results on circular array problems

Example:

python
# ✗ Wrong: Single pass
for i in range(n):
    # ...

# ✓ Correct: Double pass
for i in range(2 * n):
    idx = i % n
    # ...
    if i < n:
        stack.append(idx)

Debugging Workflow

When Your Solution Fails

Step 1: Identify the failing test case

python
# Find the smallest failing case
test_cases = [
    [2, 1, 2, 4, 3],
    [1, 2, 3],
    [3, 2, 1],
    [2, 2, 2],
]

for tc in test_cases:
    result = nextGreaterElement(tc)
    expected = compute_expected(tc)
    if result != expected:
        print(f"FAILED: {tc}")
        print(f"  Got:      {result}")
        print(f"  Expected: {expected}")
        break

Step 2: Add debug prints

python
# Add prints at key points
for i in range(n):
    print(f"i={i}, val={nums[i]}, stack={stack}")
    while stack and nums[i] > nums[stack[-1]]:
        idx = stack.pop()
        print(f"  Pop {idx}, set result[{idx}]={nums[i]}")
        result[idx] = nums[i]
    stack.append(i)

Step 3: Trace by hand

code
Write out each iteration:
  i=0: stack=[], push 0, stack=[0]
  i=1: stack=[0], 1 < 2, push 1, stack=[0,1]
  i=2: stack=[0,1], 2 > 1, pop 1, result[1]=2, ...

Step 4: Compare with expected

python
# At each step, verify against expected behavior
expected_stack_states = [
    [0],
    [0, 1],
    [0, 2],
    # ...
]

for i, expected_stack in enumerate(expected_stack_states):
    actual_stack = get_stack_at_iteration(i)
    assert actual_stack == expected_stack, f"Stack mismatch at i={i}"

Step 5: Fix and verify

python
# After fixing, test all edge cases
test_all_edge_cases()

Visualization Technique

ASCII Art Stack Trace

python
def visualize_stack(nums, stack, i):
    """
    Print ASCII visualization of stack state.
    """
    print(f"\nIteration i={i}, processing nums[{i}]={nums[i]}")
    print("Stack (bottom to top):")
    if not stack:
        print("  (empty)")
    else:
        for idx in stack:
            print(f"  [{idx}] = {nums[idx]}")
    print()

# Usage
for i in range(len(nums)):
    visualize_stack(nums, stack, i)
    # ... process ...

Output:

code
Iteration i=2, processing nums[2]=2
Stack (bottom to top):
  [0] = 2
  [1] = 1

Iteration i=3, processing nums[3]=4
Stack (bottom to top):
  [0] = 2
  [2] = 2

Checklist: Before Submitting

  • Monotonicity: Stack maintains correct order (increasing/decreasing)
  • Comparison: Using correct operator (<, >, <=, >=)
  • Initialization: Result array pre-allocated with defaults
  • Loop direction: Correct for problem (left-to-right vs right-to-left)
  • Circular handling: Double pass if array is circular
  • Edge cases: Tested empty, single, duplicates, increasing, decreasing
  • Index vs value: Storing correct type in stack
  • Boundary check: No IndexError on edge cases

Quick Debug Commands

python
# 1. Print stack at each iteration
print(f"Stack: {stack}")

# 2. Print stack with values
print(f"Stack: {[(i, nums[i]) for i in stack]}")

# 3. Verify monotonicity
if len(stack) >= 2:
    print(f"Monotonic? {nums[stack[-2]]} > {nums[stack[-1]]}")

# 4. Print result updates
print(f"Set result[{idx}] = {nums[i]}")

# 5. Verify result array
print(f"Result: {result}")

Conclusion

Debugging monotonic stack problems is systematic, not magical.

The process:

  1. Verify direction: Increasing vs decreasing
  2. Check comparison: < vs > vs <= vs >=
  3. Trace state: Print stack at each iteration
  4. Verify initialization: Pre-allocated result array
  5. Test boundaries: Edge cases

The debugging template:

python
for i in range(n):
    print(f"i={i}, val={nums[i]}, stack={stack}")
    while stack and condition:
        idx = stack.pop()
        print(f"  Popped {idx}")
        result[idx] = nums[i]
    stack.append(i)
    print(f"  Result: {result}")

Common bugs:

  • Wrong monotonicity direction
  • Wrong comparison operator
  • Not initializing result array
  • Storing values instead of indices
  • Not handling duplicates (contribution technique)

Master this debugging approach, and you'll fix any monotonic stack bug in under 5 minutes.

Next steps:

Next time your solution fails, follow this systematic process.

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