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
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 operation | Throws on failure | Special value |
|---|---|---|
| Insert | addFirst(x) | offerFirst(x) |
| Remove | removeFirst() | pollFirst(): null if empty |
| Examine | getFirst() | peekFirst(): null if empty |
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 operation | Throws on failure | Special value |
|---|---|---|
| Insert | addLast(x) | offerLast(x) |
| Remove | removeLast() | pollLast(): null if empty |
| Examine | getLast() | peekLast(): null if empty |
1static void demo() {2Deque<Integer> d = new ArrayDeque<>();3d.addLast(1); d.addLast(2); // used as a queue: 1, 24d.addFirst(9); // 9, 1, 25int a = d.pollFirst(); // 9 leaves from the front6int b = d.pollLast(); // 2 leaves from the rear7System.out.println(a + " " + b + " " + d);8}
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 as | Insert with | Remove with |
|---|---|---|
| Queue | addLast | pollFirst |
| Stack | addFirst | pollFirst |
| Deque | either end | either end |
Deque: add and remove at both ends
Interactive · edit the inputdeque · front ↔ back · size 0
An empty deque. Unlike a queue it has two working ends, and every end operation is O(1).
1Deque<Integer> d = new ArrayDeque<>();2d.addFirst(x); // F x: x becomes the new front3d.addLast(x); // L x: x becomes the new back4Integer a = d.pollFirst(); // f: remove the front, null when empty5Integer b = d.pollLast(); // l: remove the back, null when empty
Live state
size
0
Execution trace (dry run table)Show 10 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | size=0 | An empty deque. Unlike a queue it has two working ends, and every end operation is O(1). |
| 2 | L3 | op=addLast(1) size=1 | addLast(1): 1 becomes the back. |
| 3 | L3 | op=addLast(2) size=2 | addLast(2): 2 becomes the back. |
| 4 | L2 | op=addFirst(9) size=3 | addFirst(9): 9 becomes the front; the other values keep their order. |
| 5 | L2 | op=addFirst(8) size=4 | addFirst(8): 8 becomes the front; the other values keep their order. |
| 6 | L5 | op=pollLast() returned=2 size=3 | pollLast() removes and returns 2. |
| 7 | L4 | op=pollFirst() returned=8 size=2 | pollFirst() removes and returns 8. |
| 8 | L3 | op=addLast(7) size=3 | addLast(7): 7 becomes the back. |
| 9 | L4 | op=pollFirst() returned=9 size=2 | pollFirst() removes and returns 9. |
| 10 | L5 | size=2 failed=0 return=[1, 7] | Final deque (front to back): [1, 7]. |
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.
1static int[] windowMax(int[] a, int k) {2int[] out = new int[a.length - k + 1];3Deque<Integer> dq = new ArrayDeque<>(); // indices, values decreasing4for (int i = 0; i < a.length; i++) {5while (!dq.isEmpty() && dq.peekFirst() <= i - k) dq.pollFirst(); // left the window6while (!dq.isEmpty() && a[dq.peekLast()] <= a[i]) dq.pollLast(); // dominated by a[i]7dq.addLast(i);8if (i >= k - 1) out[i - k + 1] = a[dq.peekFirst()];9}10return out;11}
Best
O(n)
Average
O(n)
Worst
O(n)
Space
O(k)
Sliding window maximum (monotonic deque)
Interactive · edit the inputint[] arr · length 8
deque (front→back)
out[]
The deque keeps only indices that could still become a window maximum, with their values decreasing. The front is always the current maximum.
1public static int[] maxSlidingWindow(int[] arr, int k) {2Deque<Integer> dq = new ArrayDeque<>(); // indices; values decrease front to back3int[] out = new int[arr.length - k + 1];4for (int i = 0; i < arr.length; i++) {5if (!dq.isEmpty() && dq.peekFirst() <= i - k) dq.pollFirst(); // fell out of the window6while (!dq.isEmpty() && arr[dq.peekLast()] <= arr[i]) dq.pollLast();7dq.offerLast(i);8if (i >= k - 1) out[i - k + 1] = arr[dq.peekFirst()];9}10return out;11}
Live state
k
3
Execution trace (dry run table)Show 23 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | k=3 | The deque keeps only indices that could still become a window maximum, with their values decreasing. The front is always the current maximum. |
| 2 | L7 | i=0 | Append index 0 (1). |
| 3 | L6 | i=1 arr[i]=3 removed=0 | 1 is older and not larger than 3, so it can never be a maximum again. Remove it from the back. |
| 4 | L7 | i=1 | Append index 1 (3). |
| 5 | L7 | i=2 | Append index 2 (-1). |
| 6 | L8 | i=2 max=3 | Window [0..2] is full: its maximum is the deque front, 3. |
| 7 | L7 | i=3 | Append index 3 (-3). |
| 8 | L8 | i=3 max=3 | Window [1..3] is full: its maximum is the deque front, 3. |
| 9 | L5 | i=4 removed=1 | Index 1 has left the window [2..4], so drop it from the front. |
| 10 | L6 | i=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. |
| 11 | L6 | i=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. |
| 12 | L7 | i=4 | Append index 4 (5). |
| 13 | L8 | i=4 max=5 | Window [2..4] is full: its maximum is the deque front, 5. |
| 14 | L7 | i=5 | Append index 5 (3). |
| 15 | L8 | i=5 max=5 | Window [3..5] is full: its maximum is the deque front, 5. |
| 16 | L6 | i=6 arr[i]=6 removed=5 | 3 is older and not larger than 6, so it can never be a maximum again. Remove it from the back. |
| 17 | L6 | i=6 arr[i]=6 removed=4 | 5 is older and not larger than 6, so it can never be a maximum again. Remove it from the back. |
| 18 | L7 | i=6 | Append index 6 (6). |
| 19 | L8 | i=6 max=6 | Window [4..6] is full: its maximum is the deque front, 6. |
| 20 | L6 | i=7 arr[i]=7 removed=6 | 6 is older and not larger than 7, so it can never be a maximum again. Remove it from the back. |
| 21 | L7 | i=7 | Append index 7 (7). |
| 22 | L8 | i=7 max=7 | Window [5..7] is full: its maximum is the deque front, 7. |
| 23 | L10 | return=[3, 3, 5, 5, 6, 7] | 6 windows answered with 15 deque operations: O(n), not O(n · k). |
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”.
1static boolean isPalindrome(String s) {2Deque<Character> d = new ArrayDeque<>();3for (char c : s.toCharArray()) if (Character.isLetterOrDigit(c)) d.addLast(Character.toLowerCase(c));4while (d.size() > 1) {5if (!d.pollFirst().equals(d.pollLast())) return false; // compare the two ends, then drop both6}7return 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 inputdeque · front ↔ back · size 7
Keep only letters and digits, lower-cased: 7 characters. Skipped: 0 characters.
1static boolean isPalindrome(String s) {2Deque<Character> d = new ArrayDeque<>();3for (char c : s.toCharArray()) if (Character.isLetterOrDigit(c)) d.addLast(Character.toLowerCase(c));4while (d.size() > 1) {5if (!d.pollFirst().equals(d.pollLast())) return false; // compare the two ends, then drop both6}7return true;8}
Live state
size
7
Execution trace (dry run table)Show 5 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L3 | size=7 | Keep only letters and digits, lower-cased: 7 characters. Skipped: 0 characters. |
| 2 | L5 | front=r back=r | 'r' = 'r': the ends match, so drop both and look inward. |
| 3 | L5 | front=a back=a | 'a' = 'a': the ends match, so drop both and look inward. |
| 4 | L5 | front=c back=c | 'c' = 'c': the ends match, so drop both and look inward. |
| 5 | L7 | size=1 return=true | One 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).