← Back to DSA Course | Chapter 16: Advanced Data Structures | Lesson 7 of 7

Monotonic Queue

What is a Monotonic Queue

A monotonic queue is a deque that's kept strictly increasing or strictly decreasing from front to back at all times, which makes finding the current maximum or minimum among its elements an O(1) lookup at either end.

Example: What is a Monotonic Queue

#include <iostream>
using namespace std;
int main() {
	cout << "Deque kept strictly increasing or decreasing front-to-back -- max/min at either end is O(1)";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		System.out.println("Deque kept strictly increasing or decreasing front-to-back -- max/min at either end is O(1)");
	}
}
print("Deque kept strictly increasing or decreasing front-to-back -- max/min at either end is O(1)")
#include <stdio.h>
int main() {
	printf("Deque kept strictly increasing or decreasing front-to-back -- max/min at either end is O(1)");
	return 0;
}

Window Maximum

To track the maximum across a sliding window efficiently, a decreasing monotonic deque discards any element from the back that's smaller than the newly arriving one (since it could never be the max again while the new element is still in the window), keeping the true maximum always at the front.

Example: Window Maximum

#include <iostream>
#include <deque>
using namespace std;
int main() {
	int arr[] = {1,3,-1,-3,5,3,6,7};
	deque<int> dq;
	for (int i = 0; i < 3; i++) {
		while (!dq.empty() && arr[dq.back()] < arr[i]) dq.pop_back();
		dq.push_back(i);
	}
	cout << "Max in first window of 3: " << arr[dq.front()];
	return 0;
}
import java.util.*;
public class Main {
	public static void main(String[] args) {
		int[] arr = {1,3,-1,-3,5,3,6,7};
		Deque<Integer> dq = new ArrayDeque<>();
		for (int i = 0; i < 3; i++) {
			while (!dq.isEmpty() && arr[dq.peekLast()] < arr[i]) dq.pollLast();
			dq.addLast(i);
		}
		System.out.println("Max in first window of 3: " + arr[dq.peekFirst()]);
	}
}
from collections import deque
arr = [1,3,-1,-3,5,3,6,7]
dq = deque()
for i in range(3):
    while dq and arr[dq[-1]] < arr[i]:
        dq.pop()
    dq.append(i)
print("Max in first window of 3:", arr[dq[0]])
#include <stdio.h>
int main() {
	int arr[] = {1,3,-1,-3,5,3,6,7};
	int dq[8], front = 0, back = 0;
	for (int i = 0; i < 3; i++) {
		while (back > front && arr[dq[back-1]] < arr[i]) back--;
		dq[back++] = i;
	}
	printf("Max in first window of 3: %d", arr[dq[front]]);
	return 0;
}

Window Minimum

The same idea flips for window minimums: an increasing monotonic deque discards elements from the back that are larger than the incoming value, since a bigger element sitting behind a smaller, more recent one can never become the answer.

Example: Window Minimum

#include <iostream>
#include <deque>
using namespace std;
int main() {
	int arr[] = {1,3,-1,-3,5,3,6,7};
	deque<int> dq;
	for (int i = 0; i < 3; i++) {
		while (!dq.empty() && arr[dq.back()] > arr[i]) dq.pop_back();
		dq.push_back(i);
	}
	cout << "Min in first window of 3: " << arr[dq.front()];
	return 0;
}
import java.util.*;
public class Main {
	public static void main(String[] args) {
		int[] arr = {1,3,-1,-3,5,3,6,7};
		Deque<Integer> dq = new ArrayDeque<>();
		for (int i = 0; i < 3; i++) {
			while (!dq.isEmpty() && arr[dq.peekLast()] > arr[i]) dq.pollLast();
			dq.addLast(i);
		}
		System.out.println("Min in first window of 3: " + arr[dq.peekFirst()]);
	}
}
from collections import deque
arr = [1,3,-1,-3,5,3,6,7]
dq = deque()
for i in range(3):
    while dq and arr[dq[-1]] > arr[i]:
        dq.pop()
    dq.append(i)
print("Min in first window of 3:", arr[dq[0]])
#include <stdio.h>
int main() {
	int arr[] = {1,3,-1,-3,5,3,6,7};
	int dq[8], front = 0, back = 0;
	for (int i = 0; i < 3; i++) {
		while (back > front && arr[dq[back-1]] > arr[i]) back--;
		dq[back++] = i;
	}
	printf("Min in first window of 3: %d", arr[dq[front]]);
	return 0;
}

Deque Operations

A deque supports adding and removing elements from both its front and back in constant time, which is exactly what this technique needs — new elements enter at the back, and elements that have aged out of the window leave from the front.

Example: Deque Operations

#include <iostream>
#include <deque>
using namespace std;
int main() {
	deque<int> dq = {2,4,6};
	dq.push_back(8); dq.push_front(0); dq.pop_back(); dq.pop_front();
	cout << "Add/remove at both ends in O(1): front=" << dq.front() << " back=" << dq.back();
	return 0;
}
import java.util.*;
public class Main {
	public static void main(String[] args) {
		Deque<Integer> dq = new ArrayDeque<>(Arrays.asList(2,4,6));
		dq.addLast(8); dq.addFirst(0); dq.pollLast(); dq.pollFirst();
		System.out.println("Add/remove at both ends in O(1): front=" + dq.peekFirst() + " back=" + dq.peekLast());
	}
}
from collections import deque
dq = deque([2,4,6])
dq.append(8); dq.appendleft(0); dq.pop(); dq.popleft()
print(f"Add/remove at both ends in O(1): front={dq[0]} back={dq[-1]}")
#include <stdio.h>
int main() {
	int dq[6] = {0,2,4,6,8,0}, front = 1, back = 5;
	back--; front++;
	printf("Add/remove at both ends in O(1): front=%d back=%d", dq[front], dq[back-1]);
	return 0;
}

Complexity

Because each element is added to the deque exactly once and removed at most once over the whole scan, the total work across the entire array stays O(n), even though a naive recomputation of the window's max at every step would cost far more.

Example: Complexity

#include <iostream>
using namespace std;
int main() {
	cout << "Each element added once, removed at most once over the whole scan -- total work O(n)";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		System.out.println("Each element added once, removed at most once over the whole scan -- total work O(n)");
	}
}
print("Each element added once, removed at most once over the whole scan -- total work O(n)")
#include <stdio.h>
int main() {
	printf("Each element added once, removed at most once over the whole scan -- total work O(n)");
	return 0;
}
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 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.