šŸ“‰

Monotonic Stack / Queue

Maintain a stack or queue in sorted order to answer next-greater and sliding window max in O(n).

1 Problems2 hours1 Medium

Overview

A monotonic stack keeps elements in strictly increasing or decreasing order. When a new element breaks the order, we pop and process. This answers "next greater element", "previous smaller element", and similar range queries in O(n) instead of O(n²).

Complexity Summary

Time Complexity

O(n) — each element pushed and popped at most once

Space Complexity

O(n) for the stack

Key Patterns & Techniques

Learn the core patterns in this topic. Each block explains when to use the pattern, the intuition behind it, and a compact code example.

1

Monotonic Decreasing Stack for Next Greater

Concept

Use this when the problem asks for the next element to the right that is strictly greater than the current element

Why It Works

Maintain a stack of indices in decreasing order of their values. When a new element is larger than the element at the top index, the new element is the answer for that popped index. Push the current index after all pops.

Pattern Example
Problem

Problem Next Greater Element I. For each element in nums2, pop any index whose value is smaller than the current element and record the answer.

Implementation
// Monotonic decreasing stack — Next Greater Element I
function nextGreaterElement(nums1, nums2) {
  const nge = {}, stack = [];
  for (const n of nums2) {
    while (stack.length && stack.at(-1) < n) nge[stack.pop()] = n;
    stack.push(n);
  }
  return nums1.map(n => nge[n] ?? -1);
}
Practice questions for this pattern
2

Daily Temperatures (Days Until Warmer)

Concept

Use this as the standard application of a monotonic decreasing stack where the answer for each element is a distance rather than a value

Why It Works

Store indices on the stack, and when a warmer temperature is found, compute the day difference as i - popped index.

Pattern Example
Problem

Problem Daily Temperatures. Push index i; when temps[i] > temps[stack top], pop and set result[popped] = i - popped.

Implementation
// Daily temperatures — decreasing stack of indices, distance answer
function dailyTemperatures(temps) {
  const result = new Array(temps.length).fill(0);
  const stack = [];
  for (let i = 0; i < temps.length; i++) {
    while (stack.length && temps[i] > temps[stack.at(-1)]) {
      const idx = stack.pop();
      result[idx] = i - idx;
    }
    stack.push(i);
  }
  return result;
}
Practice questions for this pattern
3

Previous Smaller Element

Concept

Use this when you need to know, for each position, the nearest index to its left that holds a smaller value

Why It Works

Maintain a monotonic increasing stack of indices. Before pushing the current index, the stack top is the previous smaller element.

Pattern Example
Problem

Problem Sum of Subarray Minimums. For each index, the previous smaller boundary is the current stack top (or -1 if empty).

Implementation
// Previous smaller element — monotonic increasing stack
function previousSmaller(nums) {
  const pse = [], stack = [];
  for (let i = 0; i < nums.length; i++) {
    while (stack.length && nums[stack.at(-1)] >= nums[i]) stack.pop();
    pse.push(stack.length ? stack.at(-1) : -1);
    stack.push(i);
  }
  return pse;
}
Practice questions for this pattern
4

Monotonic Increasing Stack for Span

Concept

Use this when you need the number of consecutive days (or elements) to the left that are less than or equal to the current element, known as the stock span problem

Why It Works

As you pop smaller elements the span grows; the span equals the distance from the current index to the remaining stack top.

Pattern Example
Problem

Problem Stock Span. Pop while the top price is less than or equal to the current price; the span is i minus the new top (or 0 if empty).

Implementation
// Stock span — monotonic increasing stack for span widths
function stockSpan(prices) {
  const span = [], stack = [];
  for (let i = 0; i < prices.length; i++) {
    while (stack.length && prices[stack.at(-1)] <= prices[i]) stack.pop();
    span.push(stack.length ? i - stack.at(-1) : i + 1);
    stack.push(i);
  }
  return span;
}
Practice questions for this pattern
5

Trapping Rain Water via Stack

Concept

Use this when you want to compute trapped water by processing horizontal layers rather than column by column

Why It Works

A decreasing stack stores candidate left-wall indices. When a taller bar arrives, the popped bar is the floor, the stack top is the left wall, and the current bar is the right wall. Multiply the bounded height by the width to get the trapped volume.

Pattern Example
Problem

Problem Trapping Rain Water. Pop the floor bar, check that a left wall still exists, compute height and width, accumulate water.

Implementation
// Trapping rain water via stack — horizontal layer approach
function trap(height) {
  const stack = [];
  let water = 0;
  for (let i = 0; i < height.length; i++) {
    while (stack.length && height[i] > height[stack.at(-1)]) {
      const floor = stack.pop();
      if (!stack.length) break;
      const h = Math.min(height[stack.at(-1)], height[i]) - height[floor];
      const w = i - stack.at(-1) - 1;
      water += h * w;
    }
    stack.push(i);
  }
  return water;
}
Practice questions for this pattern
6

Monotonic Deque for Sliding Window Extremes

Concept

Use this when you need the maximum or minimum of every k-width window in a single O(n) pass

Why It Works

A deque stores indices in decreasing order (for max) or increasing order (for min). Before adding a new index, remove from the back any index whose value is dominated by the new element, and remove from the front any index that has fallen outside the window. The front always holds the current window extreme.

Pattern Example
Problem

Problem Sliding Window Maximum. Evict the front when it is out of range; evict the back while nums[back] <= nums[i]; then the front index is the window max.

Implementation
// Monotonic deque for sliding window max — O(n)
function maxSlidingWindow(nums, k) {
  const dq = [], res = [];
  for (let i = 0; i < nums.length; i++) {
    while (dq.length && dq[0] < i - k + 1) dq.shift();
    while (dq.length && nums[dq.at(-1)] <= nums[i]) dq.pop();
    dq.push(i);
    if (i >= k - 1) res.push(nums[dq[0]]);
  }
  return res;
}
Practice questions for this pattern
DSA Practice Online - 150+ Coding Interview Questions | LeetCode Alternative | InstaMock - AI Mock Interview