← Back to DSA Course | Chapter 5: Stacks | Lesson 5 of 5

Monotonic Stack

Monotonic Stack Idea

A monotonic stack is a stack that's kept either strictly increasing or strictly decreasing from bottom to top by popping off elements that would break that order before pushing a new one.

Example: Monotonic Stack Idea

#include <iostream>
#include <stack>
using namespace std;
int main() {
	int arr[] = {5, 3, 4, 2, 6};
	stack<int> st;
	for (int x : arr) {
		while (!st.empty() && st.top() > x) st.pop();
		st.push(x);
	}
	while (!st.empty()) { cout << st.top() << " "; st.pop(); }
	return 0;
}
import java.util.*;
public class Main {
	public static void main(String[] args) {
		int[] arr = {5, 3, 4, 2, 6};
		Deque<Integer> st = new ArrayDeque<>();
		for (int x : arr) {
			while (!st.isEmpty() && st.peek() > x) st.pop();
			st.push(x);
		}
		System.out.println(st);
	}
}
arr = [5, 3, 4, 2, 6]
stack = []
for x in arr:
    while stack and stack[-1] > x:
        stack.pop()
    stack.append(x)
print(stack)
#include <stdio.h>
int main() {
	int arr[] = {5, 3, 4, 2, 6};
	int st[5], top = -1;
	for (int i = 0; i < 5; i++) {
		while (top >= 0 && st[top] > arr[i]) top--;
		st[++top] = arr[i];
	}
	for (int i = 0; i <= top; i++) printf("%d ", st[i]);
	return 0;
}

Previous Smaller Element

To find the previous smaller element for each position, a monotonic increasing stack is scanned left to right, popping any top elements that are greater than or equal to the current value, since they can never be a 'previous smaller' for anything after this point.

Example: Previous Smaller Element

#include <iostream>
#include <stack>
using namespace std;
int main() {
	int arr[] = {4, 10, 5, 3, 8};
	stack<int> st;
	for (int i = 0; i < 5; i++) {
		while (!st.empty() && st.top() >= arr[i]) st.pop();
		cout << (st.empty() ? -1 : st.top()) << " ";
		st.push(arr[i]);
	}
	return 0;
}
import java.util.*;
public class Main {
	public static void main(String[] args) {
		int[] arr = {4, 10, 5, 3, 8};
		Deque<Integer> st = new ArrayDeque<>();
		for (int x : arr) {
			while (!st.isEmpty() && st.peek() >= x) st.pop();
			System.out.print((st.isEmpty() ? -1 : st.peek()) + " ");
			st.push(x);
		}
	}
}
arr = [4, 10, 5, 3, 8]
stack = []
for x in arr:
    while stack and stack[-1] >= x:
        stack.pop()
    print(stack[-1] if stack else -1, end=" ")
    stack.append(x)
#include <stdio.h>
int main() {
	int arr[] = {4, 10, 5, 3, 8};
	int st[5], top = -1;
	for (int i = 0; i < 5; i++) {
		while (top >= 0 && st[top] >= arr[i]) top--;
		printf("%d ", top >= 0 ? st[top] : -1);
		st[++top] = arr[i];
	}
	return 0;
}

Next Smaller Element

Finding the next smaller element uses the same monotonic-stack idea but scanning right to left (or symmetric logic left to right), popping larger elements off the top the moment a smaller value shows up.

Example: Next Smaller Element

#include <iostream>
#include <stack>
using namespace std;
int main() {
	int arr[] = {4, 10, 5, 3, 8};
	int res[5];
	stack<int> st;
	for (int i = 4; i >= 0; i--) {
		while (!st.empty() && st.top() >= arr[i]) st.pop();
		res[i] = st.empty() ? -1 : st.top();
		st.push(arr[i]);
	}
	for (int x : res) cout << x << " ";
	return 0;
}
import java.util.*;
public class Main {
	public static void main(String[] args) {
		int[] arr = {4, 10, 5, 3, 8};
		int[] res = new int[5];
		Deque<Integer> st = new ArrayDeque<>();
		for (int i = 4; i >= 0; i--) {
			while (!st.isEmpty() && st.peek() >= arr[i]) st.pop();
			res[i] = st.isEmpty() ? -1 : st.peek();
			st.push(arr[i]);
		}
		System.out.println(Arrays.toString(res));
	}
}
arr = [4, 10, 5, 3, 8]
res = [0] * 5
stack = []
for i in range(4, -1, -1):
    while stack and stack[-1] >= arr[i]:
        stack.pop()
    res[i] = stack[-1] if stack else -1
    stack.append(arr[i])
print(res)
#include <stdio.h>
int main() {
	int arr[] = {4, 10, 5, 3, 8};
	int res[5], st[5], top = -1;
	for (int i = 4; i >= 0; i--) {
		while (top >= 0 && st[top] >= arr[i]) top--;
		res[i] = top >= 0 ? st[top] : -1;
		st[++top] = arr[i];
	}
	for (int i = 0; i < 5; i++) printf("%d ", res[i]);
	return 0;
}

Histogram Concept

The largest rectangle in a histogram problem uses a monotonic increasing stack of bar heights: whenever a shorter bar appears, it pops taller bars off the stack and computes the rectangle area they could have formed.

Example: Histogram Concept

#include <iostream>
#include <stack>
using namespace std;
int main() {
	int h[] = {2, 1, 5, 6, 2, 3};
	stack<int> st;
	int maxArea = 0;
	for (int i = 0; i <= 6; i++) {
		int cur = (i == 6) ? 0 : h[i];
		while (!st.empty() && h[st.top()] >= cur) {
			int height = h[st.top()]; st.pop();
			int width = st.empty() ? i : i - st.top() - 1;
			maxArea = max(maxArea, height * width);
		}
		st.push(i);
	}
	cout << maxArea;
	return 0;
}
import java.util.*;
public class Main {
	public static void main(String[] args) {
		int[] h = {2, 1, 5, 6, 2, 3};
		Deque<Integer> st = new ArrayDeque<>();
		int maxArea = 0;
		for (int i = 0; i <= 6; i++) {
			int cur = (i == 6) ? 0 : h[i];
			while (!st.isEmpty() && h[st.peek()] >= cur) {
				int height = h[st.pop()];
				int width = st.isEmpty() ? i : i - st.peek() - 1;
				maxArea = Math.max(maxArea, height * width);
			}
			st.push(i);
		}
		System.out.println(maxArea);
	}
}
h = [2, 1, 5, 6, 2, 3]
stack = []
max_area = 0
for i in range(7):
    cur = 0 if i == 6 else h[i]
    while stack and h[stack[-1]] >= cur:
        height = h[stack.pop()]
        width = i if not stack else i - stack[-1] - 1
        max_area = max(max_area, height * width)
    stack.append(i)
print(max_area)
#include <stdio.h>
int main() {
	int h[] = {2, 1, 5, 6, 2, 3};
	int st[7], top = -1, maxArea = 0;
	for (int i = 0; i <= 6; i++) {
		int cur = (i == 6) ? 0 : h[i];
		while (top >= 0 && h[st[top]] >= cur) {
			int height = h[st[top--]];
			int width = (top < 0) ? i : i - st[top] - 1;
			if (height * width > maxArea) maxArea = height * width;
		}
		st[++top] = i;
	}
	printf("%d", maxArea);
	return 0;
}

Monotonic Stack Practice

The pattern to recognize is any problem asking for the 'nearest greater' or 'nearest smaller' element in one direction, since those almost always reduce to maintaining a monotonic stack in a single linear pass.

Example: Monotonic Stack Practice

#include <iostream>
#include <stack>
using namespace std;
int main() {
	int arr[] = {2, 1, 2, 4, 3};
	int res[5];
	stack<int> st;
	for (int i = 4; i >= 0; i--) {
		while (!st.empty() && st.top() <= arr[i]) st.pop();
		res[i] = st.empty() ? -1 : st.top();
		st.push(arr[i]);
	}
	for (int x : res) cout << x << " ";
	return 0;
}
import java.util.*;
public class Main {
	public static void main(String[] args) {
		int[] arr = {2, 1, 2, 4, 3};
		int[] res = new int[5];
		Deque<Integer> st = new ArrayDeque<>();
		for (int i = 4; i >= 0; i--) {
			while (!st.isEmpty() && st.peek() <= arr[i]) st.pop();
			res[i] = st.isEmpty() ? -1 : st.peek();
			st.push(arr[i]);
		}
		System.out.println(Arrays.toString(res));
	}
}
arr = [2, 1, 2, 4, 3]
res = [0] * 5
stack = []
for i in range(4, -1, -1):
    while stack and stack[-1] <= arr[i]:
        stack.pop()
    res[i] = stack[-1] if stack else -1
    stack.append(arr[i])
print(res)
#include <stdio.h>
int main() {
	int arr[] = {2, 1, 2, 4, 3};
	int res[5], st[5], top = -1;
	for (int i = 4; i >= 0; i--) {
		while (top >= 0 && st[top] <= arr[i]) top--;
		res[i] = top >= 0 ? st[top] : -1;
		st[++top] = arr[i];
	}
	for (int i = 0; i < 5; i++) printf("%d ", res[i]);
	return 0;
}
🔒

Chapter Quiz — Complete all 5 topics to unlock

0/5 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.