Deque Double-Ended Queue
O(1) add and remove at both front AND back. A Deque can be a stack (LIFO), a queue (FIFO), or both at once โ and it's the backbone of monotonic deque problems like Sliding Window Maximum.
What is a Deque?
A deque (pronounced "deck," short for double-ended queue) supports insertion and removal at both the front and back in O(1). A stack only allows operations at one end; a queue allows add-at-back and remove-from-front. A deque does all four: addFirst, addLast, removeFirst, removeLast โ all in constant time. In Java, ArrayDeque is the recommended implementation: it's faster than LinkedList (contiguous memory = better cache performance), faster than Stack (no synchronisation overhead), and implements both the Deque and Queue interfaces. In interviews, a deque appears in three contexts: (1) as a drop-in replacement for both Stack and Queue (use ArrayDeque for everything), (2) as the foundation for the monotonic deque pattern (Sliding Window Maximum, LC 239), and (3) in problems that need efficient front+back access (palindrome checking on a character deque, BFS with 0/1 edge weights using 0-1 BFS).
How It Thinks
Core Operations
| Operation | Throws Exception | Returns Special Value | Time | Description |
|---|---|---|---|---|
| Insert front | addFirst(e) | offerFirst(e) | O(1)* | Add element at front |
| Insert back | addLast(e) | offerLast(e) | O(1)* | Add element at back |
| Remove front | removeFirst() | pollFirst() | O(1) | Remove and return front element |
| Remove back | removeLast() | pollLast() | O(1) | Remove and return back element |
| Peek front | getFirst() | peekFirst() | O(1) | View front without removing |
| Peek back | getLast() | peekLast() | O(1) | View back without removing |
| Size | size() | O(1) | Number of elements | |
| Search | contains(o) | O(n) | Linear scan โ deque is not indexed | |
* Amortised O(1) โ occasional O(n) resize when backing array is full.
ArrayDeque vs LinkedList
Circular Array โ โ Preferred
- Contiguous memory โ cache-friendly
- No per-node Object allocation
- ~3ร faster than LinkedList in benchmarks
- Amortised O(1) โ occasional resize
- Does NOT allow null elements
- NOT thread-safe (no sync overhead)
Doubly-Linked Nodes
- True O(1) โ no resize ever
- Each node = separate heap allocation
- Poor cache locality โ pointer chasing
- 16+ bytes overhead per node
- Allows null elements
- Also implements List interface
- Always use
ArrayDequeunless the problem specifically requires null storage or iterator-based middle removal. - The Java docs themselves state: "This class is likely to be faster than Stack when used as a stack, and faster than LinkedList when used as a queue."
Deque as Stack and Queue
In interviews, use ArrayDeque for both stack and queue operations. Map the abstract operations to deque methods:
| Behaviour | Abstract Op | Deque Method | End Used |
|---|---|---|---|
| Stack (LIFO) | push | push(e) / addFirst(e) | Front |
| pop | pop() / removeFirst() | Front | |
| peek | peek() / peekFirst() | Front | |
| Queue (FIFO) | enqueue | offer(e) / addLast(e) | Back |
| dequeue | poll() / removeFirst() | Front | |
| peek | peek() / peekFirst() | Front |
Stack class. It extends Vector, which means every operation is synchronized โ unnecessary overhead for single-threaded interview code. The Java docs explicitly recommend ArrayDeque over Stack. Use Deque<Integer> stack = new ArrayDeque<>();.
Monotonic Deque โ Sliding Window Maximum
The monotonic deque is the killer application of a deque. It maintains indices in decreasing order of their values (for maximum queries). The front always holds the index of the current window's maximum. When a new element enters, pop all back elements that are smaller โ they can never be the answer because the new element is both larger AND newer. When the front element falls outside the window, pop it from the front. Each element enters and exits the deque at most once โ O(n) total.
- A monotonic stack finds next greater/smaller element.
- A monotonic deque finds the max/min in a sliding window.
- The deque needs front-removal (to expire old elements) which a stack can't do โ that's why it's a deque, not a stack.
When to Reach for a Deque
| Signal | Use As | Example |
|---|---|---|
| Need stack operations (push/pop/peek) | Stack via push/pop/peek | Valid Parentheses, Daily Temperatures |
| Need queue operations (FIFO) | Queue via offer/poll/peek | BFS traversal, task scheduling |
| "sliding window max/min" | Monotonic deque | Sliding Window Maximum (LC 239) |
| "max/min in a range that slides" | Monotonic deque | Shortest Subarray with Sum โฅ K (LC 862) |
| 0-1 BFS (edges with weight 0 or 1) | Full deque (addFirst for 0-weight, addLast for 1-weight) | Minimum Cost to Make at Least One Valid Path (LC 1368) |
| Palindrome check on characters | Full deque (compare front and back) | Check palindrome after deletions |
- In interviews, declare
Deque<Integer> dq = new ArrayDeque<>();for ALL stack and queue needs. - Only reach for
PriorityQueuewhen you need heap ordering, orLinkedListwhen you need null storage or iterator-based removal.
Build It Once
Build the sliding window maximum once โ it's the only deque-specific algorithm you need. For stack/queue usage, just use ArrayDeque with the right method names.
Use It In Java
import java.util.ArrayDeque; / import java.util.Deque; Deque<Integer> dq = new ArrayDeque<>(); โ always use interface type new ArrayDeque<>(capacity) โ optional, default 16, doubles when full | Method | Behaviour | On Empty | Use as |
|---|---|---|---|
push(e) | addFirst(e) | โ | Stack push |
pop() | removeFirst() | throws | Stack pop |
offer(e) | addLast(e) | โ | Queue enqueue |
poll() | removeFirst() | null | Queue dequeue |
peek() | peekFirst() | null | Both peek |
offerFirst(e) | Insert at front | โ | Deque-specific |
offerLast(e) | Insert at back | โ | Deque-specific |
pollFirst() | Remove from front | null | Deque-specific |
pollLast() | Remove from back | null | Deque-specific |
peekFirst() | View front | null | Deque-specific |
peekLast() | View back | null | Deque-specific |
push() adds to the FRONT (it's a stack method โ LIFO). offer() adds to the BACK (it's a queue method โ FIFO). If you use both on the same deque, elements go to different ends. Pick one convention: push/pop/peek for stack, offer/poll/peek for queue, offerFirst/offerLast/pollFirst/pollLast for full deque.
ArrayDeque does NOT implement the List interface โ no get(index), no set(index, value). It's a sequential-access structure. If you need indexed access, use an ArrayList. Deque is for end-operations only.
Problems To Solve
Most "deque" interview problems are actually monotonic deque problems โ sliding window maximum and its variants. The remaining uses are as a cleaner stack/queue replacement. If you master Sliding Window Maximum, you've covered 80% of deque interviews.
| Difficulty | Pattern | Problem | Key Insight |
|---|---|---|---|
| Hard | mono-deque | Sliding Window Maximum โ LC 239 | Decreasing monotonic deque of indices. Front = current window max. Pop expired front, pop smaller back. O(n). |
| Hard | mono-deque | Shortest Subarray with Sum โฅ K โ LC 862 | Prefix sum + monotonic deque. Deque maintains increasing prefix sums. Pop front when current prefix minus front prefix โฅ K. |
| Medium | deque | Design Circular Deque โ LC 641 | Implement a fixed-capacity circular deque with insertFront, insertLast, deleteFront, deleteLast. Circular array with head/tail pointers. |
| Hard | mono-deque | Constrained Subsequence Sum โ LC 1425 | DP + monotonic deque. dp[i] = max(dp[j]) + nums[i] for j in [i-k, i-1]. Deque maintains max dp values in the sliding window. |
| Medium | deque | Design Front Middle Back Queue โ LC 1670 | Two deques: front half and back half. Balance sizes after every operation to maintain O(1) middle access. |
| Easy | stack/queue | Implement Queue using Stacks โ LC 232 | Use two ArrayDeques as stacks. Push to one, lazy-transfer to the other on poll. Amortised O(1). |
- When you see "maximum/minimum in a sliding window" or "optimise DP with a sliding window constraint," think monotonic deque.
- The template is always: (1) pop expired indices from the front, (2) pop dominated values from the back, (3) push current index, (4) front is the answer.
- For everything else,
ArrayDequeis just your default stack/queue implementation.