Chapter 13: Queues, Deques and Circular Buffers
13.04Beginner / Intermediate5 topics14 min3 interactive animations

Deque operations

The double-ended queue: adding and removing at both ends in O(1), the Java Deque methods, and two classic uses, the sliding window maximum and the palindrome check.

Learning goals

  • Use the first/last methods of Deque correctly
  • Explain how a deque generalises both a stack and a queue
  • Trace the sliding window maximum and the palindrome check with a deque

Prerequisites

Queue implementationsCircular buffers
13.04.1

Add/remove first

A deque (pronounced “deck”) supports insertion and removal at both ends. At the front, addFirst / offerFirst insert, removeFirst / pollFirst remove and getFirst / peekFirst examine. As with Queue, the first name in each pair throws an exception on failure and the second returns a special value.

Deque.push(x) is simply addFirst(x) and pop() is removeFirst(), which is how an ArrayDeque works as a stack.

Front operationThrows on failureSpecial value
InsertaddFirst(x)offerFirst(x)
RemoveremoveFirst()pollFirst(): null if empty
ExaminegetFirst()peekFirst(): null if empty
13.04.2

Add/remove last

The rear mirrors the front: addLast / offerLast, removeLast / pollLast, getLast / peekLast. The plain Queue methods add, offer and peek are the same as the *Last variants for insertion and the *First variants for removal, so a deque used as a queue enqueues at the last end and dequeues at the first end.

Rear operationThrows on failureSpecial value
InsertaddLast(x)offerLast(x)
RemoveremoveLast()pollLast(): null if empty
ExaminegetLast()peekLast(): null if empty
Java
1static void demo() {
2 Deque<Integer> d = new ArrayDeque<>();
3 d.addLast(1); d.addLast(2); // used as a queue: 1, 2
4 d.addFirst(9); // 9, 1, 2
5 int a = d.pollFirst(); // 9 leaves from the front
6 int b = d.pollLast(); // 2 leaves from the rear
7 System.out.println(a + " " + b + " " + d);
8}
13.04.3

Double-ended queue

A deque is the most general of the three structures. Using only addLast and pollFirst it is a queue; using only addFirst and pollFirst it is a stack; using all four it is something neither can be. Every end operation is O(1) in a circular-array or linked implementation.

In the animation each operation is one token: F5 is addFirst(5), L5 is addLast(5), f is pollFirst() and l is pollLast(). Try “Used as a stack” and “Used as a queue”: the same structure, two different orders of removal.

Used asInsert withRemove with
QueueaddLastpollFirst
StackaddFirstpollFirst
Dequeeither endeither end

Deque: add and remove at both ends

Interactive · edit the input

Your input

Try

deque · front ↔ back · size 0

empty
Setup

An empty deque. Unlike a queue it has two working ends, and every end operation is O(1).

Step 1 / 10
Java
1Deque<Integer> d = new ArrayDeque<>();
2d.addFirst(x); // F x: x becomes the new front
3d.addLast(x); // L x: x becomes the new back
4Integer a = d.pollFirst(); // f: remove the front, null when empty
5Integer b = d.pollLast(); // l: remove the back, null when empty

Live state

size

0

No condition evaluated on this step
Execution trace (dry run table)Show 10 steps
StepLineStateWhat happens
1L1size=0An empty deque. Unlike a queue it has two working ends, and every end operation is O(1).
2L3op=addLast(1) size=1addLast(1): 1 becomes the back.
3L3op=addLast(2) size=2addLast(2): 2 becomes the back.
4L2op=addFirst(9) size=3addFirst(9): 9 becomes the front; the other values keep their order.
5L2op=addFirst(8) size=4addFirst(8): 8 becomes the front; the other values keep their order.
6L5op=pollLast() returned=2 size=3pollLast() removes and returns 2.
7L4op=pollFirst() returned=8 size=2pollFirst() removes and returns 8.
8L3op=addLast(7) size=3addLast(7): 7 becomes the back.
9L4op=pollFirst() returned=9 size=2pollFirst() removes and returns 9.
10L5size=2 failed=0 return=[1, 7]Final deque (front to back): [1, 7].
13.04.4

Sliding window deque

The sliding window maximum is the classic deque algorithm. The deque stores *indices* of window elements in decreasing order of their values. A new element first pops every smaller value from the back (they can never be a maximum again, because the new value is larger and will stay in the window longer), then joins the back. The front is the current maximum; it is removed from the front when its index leaves the window.

Each index enters and leaves the deque at most once, so the whole scan is O(n) even though a window may be large. Step through the “Classic” preset and watch the values being discarded from the back.

Java
1static int[] windowMax(int[] a, int k) {
2 int[] out = new int[a.length - k + 1];
3 Deque<Integer> dq = new ArrayDeque<>(); // indices, values decreasing
4 for (int i = 0; i < a.length; i++) {
5 while (!dq.isEmpty() && dq.peekFirst() <= i - k) dq.pollFirst(); // left the window
6 while (!dq.isEmpty() && a[dq.peekLast()] <= a[i]) dq.pollLast(); // dominated by a[i]
7 dq.addLast(i);
8 if (i >= k - 1) out[i - k + 1] = a[dq.peekFirst()];
9 }
10 return out;
11}

Best

O(n)

Average

O(n)

Worst

O(n)

Space

O(k)

Sliding window maximum (monotonic deque)

Interactive · edit the input

Your input

Try

int[] arr · length 8

1
3
-1
-3
5
3
6
7
01234567
EnteringIn the dequeWindow maximum

deque (front→back)

empty

out[]

empty
Setup

The deque keeps only indices that could still become a window maximum, with their values decreasing. The front is always the current maximum.

Step 1 / 23
Java
1public static int[] maxSlidingWindow(int[] arr, int k) {
2 Deque<Integer> dq = new ArrayDeque<>(); // indices; values decrease front to back
3 int[] out = new int[arr.length - k + 1];
4 for (int i = 0; i < arr.length; i++) {
5 if (!dq.isEmpty() && dq.peekFirst() <= i - k) dq.pollFirst(); // fell out of the window
6 while (!dq.isEmpty() && arr[dq.peekLast()] <= arr[i]) dq.pollLast();
7 dq.offerLast(i);
8 if (i >= k - 1) out[i - k + 1] = arr[dq.peekFirst()];
9 }
10 return out;
11}

Live state

0 deque operations

k

3

No condition evaluated on this step
Execution trace (dry run table)Show 23 steps
StepLineStateWhat happens
1L2k=3The deque keeps only indices that could still become a window maximum, with their values decreasing. The front is always the current maximum.
2L7i=0Append index 0 (1).
3L6i=1 arr[i]=3 removed=01 is older and not larger than 3, so it can never be a maximum again. Remove it from the back.
4L7i=1Append index 1 (3).
5L7i=2Append index 2 (-1).
6L8i=2 max=3Window [0..2] is full: its maximum is the deque front, 3.
7L7i=3Append index 3 (-3).
8L8i=3 max=3Window [1..3] is full: its maximum is the deque front, 3.
9L5i=4 removed=1Index 1 has left the window [2..4], so drop it from the front.
10L6i=4 arr[i]=5 removed=3-3 is older and not larger than 5, so it can never be a maximum again. Remove it from the back.
11L6i=4 arr[i]=5 removed=2-1 is older and not larger than 5, so it can never be a maximum again. Remove it from the back.
12L7i=4Append index 4 (5).
13L8i=4 max=5Window [2..4] is full: its maximum is the deque front, 5.
14L7i=5Append index 5 (3).
15L8i=5 max=5Window [3..5] is full: its maximum is the deque front, 5.
16L6i=6 arr[i]=6 removed=53 is older and not larger than 6, so it can never be a maximum again. Remove it from the back.
17L6i=6 arr[i]=6 removed=45 is older and not larger than 6, so it can never be a maximum again. Remove it from the back.
18L7i=6Append index 6 (6).
19L8i=6 max=6Window [4..6] is full: its maximum is the deque front, 6.
20L6i=7 arr[i]=7 removed=66 is older and not larger than 7, so it can never be a maximum again. Remove it from the back.
21L7i=7Append index 7 (7).
22L8i=7 max=7Window [5..7] is full: its maximum is the deque front, 7.
23L10return=[3, 3, 5, 5, 6, 7]6 windows answered with 15 deque operations: O(n), not O(n · k).
13.04.5

Palindrome deque

A palindrome reads the same from both ends, so a deque fits naturally: keep only letters and digits, then repeatedly compare the front and the back and drop both. If every pair matches the text is a palindrome; one mismatch ends the search.

Compare the two characters with equals, not ==: d.pollFirst() returns a Character object, and == on boxed values compares references. The animation skips punctuation, as in “A man, a plan, a canal: Panama”.

Java
1static boolean isPalindrome(String s) {
2 Deque<Character> d = new ArrayDeque<>();
3 for (char c : s.toCharArray()) if (Character.isLetterOrDigit(c)) d.addLast(Character.toLowerCase(c));
4 while (d.size() > 1) {
5 if (!d.pollFirst().equals(d.pollLast())) return false; // compare the two ends, then drop both
6 }
7 return true;
8}

Best

O(1)

Average

O(n)

Worst

O(n)

Space

O(n)

Palindrome check by comparing both ends of a deque

Interactive · edit the input

Your input

Try

deque · front ↔ back · size 7

rfront
a
c
e
c
a
rback
Setup

Keep only letters and digits, lower-cased: 7 characters. Skipped: 0 characters.

Step 1 / 5
Java
1static boolean isPalindrome(String s) {
2 Deque<Character> d = new ArrayDeque<>();
3 for (char c : s.toCharArray()) if (Character.isLetterOrDigit(c)) d.addLast(Character.toLowerCase(c));
4 while (d.size() > 1) {
5 if (!d.pollFirst().equals(d.pollLast())) return false; // compare the two ends, then drop both
6 }
7 return true;
8}

Live state

size

7

No condition evaluated on this step
Execution trace (dry run table)Show 5 steps
StepLineStateWhat happens
1L3size=7Keep only letters and digits, lower-cased: 7 characters. Skipped: 0 characters.
2L5front=r back=r'r' = 'r': the ends match, so drop both and look inward.
3L5front=a back=a'a' = 'a': the ends match, so drop both and look inward.
4L5front=c back=c'c' = 'c': the ends match, so drop both and look inward.
5L7size=1 return=trueOne character is left in the middle: it matches itself. Return true.

Practice ladder

Now try it yourself

Easy

  • Show the deque after F1 L2 F3 f l
  • Which methods turn an ArrayDeque into a stack?

Medium

  • Sliding Window Maximum
  • Design Circular Deque

Advanced

  • Shortest Subarray with Sum at Least K
  • Constrained Subsequence Sum

Key takeaways

  • A deque has O(1) insert, remove and peek at both ends.
  • Queue, stack and deque are one structure used with different method subsets.
  • A monotonic deque of indices solves sliding window maximum in O(n).