Deque (Double-Ended Queue)
Data Structure · Starter
Step through it: Sliding Window pattern
Window "a", length 1
best so far: 1
Step 1 of 6
Goal: find the longest stretch of letters with no letter repeated. Checking every stretch is slow. Instead keep one window over the string and only ever slide it forward.
Step 2 of 6
Grow the window to the right while each new letter is different. "abc" has no repeats, so its length, 3, is the best so far.
Step 3 of 6
The next letter is "a", but the window already has an "a". The window is no longer valid.
Step 4 of 6
Fix it by shrinking from the left until the repeat is gone. Dropping the first "a" leaves "bca", valid again.
Step 5 of 6
Keep growing: add "d", "e", "f". The best is now "bcadef", length 6. The left edge and right edge only ever move forward, so each letter is added once and dropped at most once. This is called a sliding window.
Step 6 of 6
Your turn. Pick a word and press Next to watch the window grow and shrink.
Complexity
| Operation | Time |
|---|---|
addFront / addBack | O(1) |
removeFront / removeBack | O(1) |
peekFront / peekBack | O(1) |
isEmpty / size | O(1) |
| Space | O(n) |
A deque (pronounced "deck", short for double-ended queue) is a linear data structure that generalizes both a stack and a queue. Elements can be added or removed from either end — the front (head) or the back (tail) — in O(1) time.
addFront(3) addBack(4)
↓ ↓
┌───────┬───────┬───────┬───────┐
│ 3 │ 1 │ 2 │ 4 │ ← internal doubly linked list
└───────┴───────┴───────┴───────┘
↑ ↑
removeFront() removeBack()Operations
| Method | Description | Time |
|---|---|---|
addFront(v) | Insert element v at the front | O(1) |
addBack(v) | Insert element v at the back | O(1) |
removeFront() | Remove and return the front element | O(1) |
removeBack() | Remove and return the back element | O(1) |
peekFront() | Return the front element without removing it | O(1) |
peekBack() | Return the back element without removing it | O(1) |
isEmpty() | Return true if the deque has no elements | O(1) |
size | Return the number of elements | O(1) |
Note:
sizeis O(1) because the deque keeps a running element count that is updated on every add and remove operation.
Use Cases
A deque is the right tool when you need O(1) access at both ends:
- Sliding window maximum/minimum — maintain candidates in a monotonic deque so each element is pushed and popped at most once (overall O(n)).
- Browser history — navigate backward (
removeFront) and forward (removeBack) through pages. - Undo / redo stacks — push actions to the back, undo from the back, redo from the front.
- Palindrome checking — compare characters from both ends simultaneously.
- Work-stealing schedulers (e.g. Java's
ForkJoinPool) — threads push/pop from their own back, while idle threads steal from another thread's front.
Implementation Note
This implementation is backed by the project's existing
DoublyLinkedList. Because every node keeps a reference
to both its previous and next neighbours, this gives O(1) prepend (for
addFront), O(1) deleteHead (for removeFront) and O(1) append /
deleteTail (for addBack / removeBack), with no need to shift array
elements. The number of elements is tracked in a counter, so size is O(1) too.
An alternative implementation using a circular buffer (fixed-size array) offers better cache locality but requires resizing logic. The linked-list approach is chosen here to stay consistent with the other data structures in this project.
Code
import DoublyLinkedList from '../doubly-linked-list/DoublyLinkedList';
export default class Deque {
constructor() {
// We use a doubly linked list internally so that both front and back
// operations (add, remove and peek) run in O(1) time.
this.linkedList = new DoublyLinkedList();
// Keep a running element count so that `size` stays O(1).
this.length = 0;
}
/**
* Check if the deque is empty.
* @return {boolean}
*/
isEmpty() {
return !this.linkedList.head;
}
/**
* Read the element at the front of the deque without removing it.
* @return {*}
*/
peekFront() {
if (!this.linkedList.head) {
return null;
}
return this.linkedList.head.value;
}
/**
* Read the element at the back of the deque without removing it.
* @return {*}
*/
peekBack() {
if (!this.linkedList.tail) {
return null;
}
return this.linkedList.tail.value;
}
/**
* Add a new element to the front (head) of the deque.
* @param {*} value
*/
addFront(value) {
this.linkedList.prepend(value);
this.length += 1;
}
/**
* Add a new element to the back (tail) of the deque.
* @param {*} value
*/
addBack(value) {
this.linkedList.append(value);
this.length += 1;
}
/**
* Remove the element from the front (head) of the deque.
* @return {*}
*/
removeFront() {
const removedHead = this.linkedList.deleteHead();
if (!removedHead) {
return null;
}
this.length -= 1;
return removedHead.value;
}
/**
* Remove the element from the back (tail) of the deque.
* @return {*}
*/
removeBack() {
const removedTail = this.linkedList.deleteTail();
if (!removedTail) {
return null;
}
this.length -= 1;
return removedTail.value;
}
/**
* Return the number of elements in the deque.
* @return {number}
*/
get size() {
return this.length;
}
/**
* Convert the deque to an array (front to back).
* @return {*[]}
*/
toArray() {
return this.linkedList
.toArray()
.map((linkedListNode) => linkedListNode.value);
}
/**
* Return a string representation of the deque.
* @param {function} [callback]
* @return {string}
*/
toString(callback) {
return this.linkedList.toString(callback);
}
}References
Practice
Sliding Window
- Best Time to Buy and Sell StockEasy
- Maximum Average Subarray IEasy
- Contains Duplicate IIEasy
- Longest Nice SubarrayEasy
- Longest Substring Without Repeating CharactersMedium
- Longest Repeating Character ReplacementMedium
- Permutation in StringMedium
- Find All Anagrams in a StringMedium
- Minimum Size Subarray SumMedium
- Max Consecutive Ones IIIMedium
- Fruit Into BasketsMedium
- Longest Substring with At Most K Distinct CharactersMedium
- Subarray Product Less Than KMedium
- Maximum Points You Can Obtain from CardsMedium
- Count Number of Nice SubarraysMedium
- Minimum Window SubstringHard
- Sliding Window MaximumHard
- Substring with Concatenation of All WordsHard
Spotted a mistake or something unclear? Report it on GitHub.