Deque Data Structure Explained

Learn Deque Data Structure Explained with practical examples, explanations, best practices, and real-world coding techniques.

What Is a Deque?

A deque, pronounced "deck", is a linear data structure that allows elements to be inserted and removed from both ends of the sequence. The name comes from double-ended queue.

Unlike a standard queue, which normally adds elements at the rear and removes them from the front, a deque supports operations at both the front and the rear. This makes it useful when an application needs more flexibility in how elements enter or leave a data structure.

Front                                  Rear
  ↓                                      ↓
[ 10 ] [ 20 ] [ 30 ] [ 40 ]

Add/remove                          Add/remove
from this end                       from this end
     ↕                                   ↕

A deque can therefore behave like a queue, a stack, or a combination of both, depending on which operations an application performs.

Deque vs Standard Queue

A standard queue follows a more restricted access pattern: elements are inserted at the rear and removed from the front. A deque removes that restriction by allowing operations at both ends.

Operation Queue Deque
Add at front Normally not supported Supported
Add at rear Supported Supported
Remove from front Supported Supported
Remove from rear Normally not supported Supported

Core Deque Operations

A deque generally provides four fundamental insertion and deletion operations. Additional operations such as peek, size, and empty checks are commonly added as well.

Operation Purpose
addFront Adds an element to the front
addRear Adds an element to the rear
removeFront Removes the front element
removeRear Removes the rear element
peekFront Reads the front element
peekRear Reads the rear element
isEmpty Checks whether the deque contains elements

Adding Elements at the Front

The addFront operation inserts a new element before the current first element. Existing elements maintain their relative order.

Deque:
Front → [20] [30] [40] ← Rear

addFront(10)

Deque:
Front → [10] [20] [30] [40] ← Rear

Adding Elements at the Rear

The addRear operation appends a new element after the current rear element.

Deque:
Front → [10] [20] [30] ← Rear

addRear(40)

Deque:
Front → [10] [20] [30] [40] ← Rear

Removing from the Front

The removeFront operation removes the first element from the deque. This is equivalent to the normal dequeue operation of a standard queue.

Deque:
Front → [10] [20] [30] [40] ← Rear

removeFront()

Removed: 10

Deque:
Front → [20] [30] [40] ← Rear

Removing from the Rear

The removeRear operation removes the last element from the deque. This is the operation that distinguishes a deque from a standard queue.

Deque:
Front → [10] [20] [30] [40] ← Rear

removeRear()

Removed: 40

Deque:
Front → [10] [20] [30] ← Rear

Deque as Both a Queue and a Stack

A deque can model the behavior of a standard queue by adding at the rear and removing from the front. It can also model a stack by adding and removing from the same end.

Behavior Deque Operations
Queue addRear + removeFront
Stack addRear + removeRear

This flexibility is one of the main reasons deques appear in algorithms and systems that need to change how elements are processed depending on the situation.

Deque Using a JavaScript Array

A JavaScript array provides convenient methods such as push(), pop(), unshift(), and shift(). These methods can demonstrate deque behavior, although operations at the beginning of a JavaScript array may require shifting or reindexing elements.

const deque = [];

deque.push(20);
deque.push(30);

deque.unshift(10);

console.log(deque);
// [10, 20, 30]

deque.push(40);

console.log(deque);
// [10, 20, 30, 40]

The example is easy to understand, but it is not necessarily the most efficient implementation for large workloads because unshift() and shift() can require work proportional to the number of stored elements.

Efficient Deque with a Circular Array

A circular array can provide efficient operations at both ends. The implementation maintains front and rear positions and uses modulo arithmetic to wrap indexes around the underlying array.

Circular storage:

[  ] [  ] [  ] [  ] [  ]

Front and rear can move in either direction:

← Front                       Rear →
   └────── positions wrap ──────┘

This approach is particularly useful for fixed-capacity deques because positions released from one end can be reused when new elements are inserted at either end.

Linked-List Implementation

A doubly linked list is another natural way to implement a deque. Each node maintains references to both its previous and next nodes, allowing elements to be inserted or removed from either end without traversing the entire list.

             previous       next
                ←             →
Front → [10] ⇄ [20] ⇄ [30] ⇄ [40] ← Rear

The front reference points to the first node and the rear reference points to the last node. Because each node knows its neighbors, both ends can be updated directly.

Deque with a Doubly Linked List

The following implementation demonstrates the basic structure of a deque using a doubly linked list.

class Node {
  constructor(value) {
    this.value = value;
    this.prev = null;
    this.next = null;
  }
}

class Deque {
  constructor() {
    this.front = null;
    this.rear = null;
    this.count = 0;
  }

  addFront(value) {
    const node = new Node(value);

    if (this.front === null) {
      this.front = node;
      this.rear = node;
    } else {
      node.next = this.front;
      this.front.prev = node;
      this.front = node;
    }

    this.count++;
  }

  addRear(value) {
    const node = new Node(value);

    if (this.rear === null) {
      this.front = node;
      this.rear = node;
    } else {
      node.prev = this.rear;
      this.rear.next = node;
      this.rear = node;
    }

    this.count++;
  }

  removeFront() {
    if (this.front === null) {
      return null;
    }

    const value = this.front.value;
    this.front = this.front.next;

    if (this.front === null) {
      this.rear = null;
    } else {
      this.front.prev = null;
    }

    this.count--;
    return value;
  }

  removeRear() {
    if (this.rear === null) {
      return null;
    }

    const value = this.rear.value;
    this.rear = this.rear.prev;

    if (this.rear === null) {
      this.front = null;
    } else {
      this.rear.next = null;
    }

    this.count--;
    return value;
  }

  isEmpty() {
    return this.count === 0;
  }

  size() {
    return this.count;
  }
}

Using the Linked-List Deque

Once the deque is implemented, elements can be inserted or removed from either end as required.

const deque = new Deque();

deque.addRear(20);
deque.addRear(30);
deque.addFront(10);
deque.addRear(40);

console.log(deque.removeFront()); // 10
console.log(deque.removeRear());  // 40
console.log(deque.removeFront()); // 20
console.log(deque.removeRear());  // 30

Deque Complexity

A properly implemented deque can provide constant-time operations at both ends. A doubly linked list is one common way to achieve this because the front and rear nodes are directly accessible.

Operation Typical Complexity
Add Front O(1)
Add Rear O(1)
Remove Front O(1)
Remove Rear O(1)
Peek Front O(1)
Peek Rear O(1)
Search O(n)

Input-Restricted and Output-Restricted Deques

Not every deque implementation needs to expose all four insertion and deletion operations. Two restricted forms are commonly discussed in data structure theory.

Input-Restricted Deque

An input-restricted deque allows insertion at only one end but permits deletion from both ends.

Insertion:
Front ✕
Rear  ✓

Deletion:
Front ✓
Rear  ✓

Output-Restricted Deque

An output-restricted deque allows insertion at both ends but permits deletion from only one end.

Insertion:
Front ✓
Rear  ✓

Deletion:
Front ✓
Rear  ✕

These restricted variants are useful for understanding how different combinations of operations can produce specialized queue behavior.

Applications of Deques

Because a deque supports both ends, it is useful for algorithms and systems where the next element to process may need to be selected from either side.

  • Sliding window algorithms: Maintain candidates entering and leaving a moving window.
  • Palindrome checking: Compare characters from the front and rear.
  • Task scheduling: Add urgent work to one end and normal work to another.
  • Browser history or navigation models: Maintain sequences where elements may be managed from either side.
  • Graph algorithms: Certain search strategies can benefit from adding and removing nodes at both ends.
  • Undo or processing buffers: Allow work to be added or consumed from different ends depending on application rules.

Deque for Palindrome Checking

A deque provides a natural model for checking whether a sequence is a palindrome. The algorithm compares the first and last characters, removes both, and continues until the sequence is exhausted.

Input: "level"

Front → l e v e l ← Rear

Compare:
l == l
e == e

Middle:
v

Result: palindrome

Deque in Sliding Window Problems

Monotonic deques are frequently used in sliding-window problems. They allow an algorithm to remove outdated elements from the front while removing less useful candidates from the rear.

For example, when calculating the maximum value in every window of a fixed size, a deque can maintain candidate values in decreasing order. Elements that fall outside the current window are removed from the front, while smaller candidates can be removed from the rear.

Input:
[1, 3, -1, -3, 5, 3, 6, 7]

Window size = 3

The deque stores useful candidates
rather than every value in the current window.

This technique can reduce the time required for many sliding-window maximum or minimum problems from a straightforward O(n × k) approach to O(n).

Deque vs Queue vs Stack

These three structures are closely related but provide different access rules.

Structure Insertion Removal Typical Principle
Stack One end Same end LIFO
Queue Rear Front FIFO
Deque Both ends Both ends Flexible end-based access

Common Mistakes

  • Treating a deque as if elements can be inserted or removed from arbitrary middle positions.
  • Using array shift() or unshift() repeatedly without considering their cost.
  • Forgetting to update both front and rear references when the deque becomes empty.
  • Breaking the previous and next links when implementing a deque with a doubly linked list.
  • Confusing a deque with a priority queue. A deque chooses an end; a priority queue chooses according to priority.
  • Using a circular implementation without clearly defining how full and empty states are represented.

When Should You Use a Deque?

A deque is a good fit when the algorithm needs efficient access to both ends of a sequence. It is particularly useful when elements can become candidates for removal from either side.

  • You need insertion at both ends.
  • You need deletion at both ends.
  • You are implementing a sliding-window algorithm.
  • You need a structure that can behave as either a stack or a queue.
  • The application needs flexible end-based scheduling or buffering.

Key Takeaways

  • A deque is a double-ended queue that supports operations at both ends.
  • Elements can be added and removed from both the front and rear.
  • A deque can model both queue and stack behavior.
  • Doubly linked lists and circular arrays are common implementation choices.
  • A properly implemented deque can perform all four primary end operations in O(1) time.
  • Deques are useful in sliding-window algorithms, palindrome checking, scheduling, and buffering.
  • Restricted deques limit insertion or deletion to one end.

Code Examples

class Node {
  constructor(value) {
    this.value = value;
    this.prev = null;
    this.next = null;
  }
}

class Deque {
  constructor() {
    this.front = null;
    this.rear = null;
    this.count = 0;
  }

  addFront(value) {
    const node = new Node(value);

    if (this.front === null) {
      this.front = node;
      this.rear = node;
    } else {
      node.next = this.front;
      this.front.prev = node;
      this.front = node;
    }

    this.count++;
  }

  addRear(value) {
    const node = new Node(value);

    if (this.rear === null) {
      this.front = node;
      this.rear = node;
    } else {
      node.prev = this.rear;
      this.rear.next = node;
      this.rear = node;
    }

    this.count++;
  }

  removeFront() {
    if (this.front === null) {
      return null;
    }

    const value = this.front.value;
    this.front = this.front.next;

    if (this.front === null) {
      this.rear = null;
    } else {
      this.front.prev = null;
    }

    this.count--;
    return value;
  }

  removeRear() {
    if (this.rear === null) {
      return null;
    }

    const value = this.rear.value;
    this.rear = this.rear.prev;

    if (this.rear === null) {
      this.front = null;
    } else {
      this.rear.next = null;
    }

    this.count--;
    return value;
  }
}

const deque = new Deque();

deque.addRear(20);
deque.addFront(10);
deque.addRear(30);

console.log(deque.removeFront()); // 10
console.log(deque.removeRear());  // 30
    
const deque = [];

deque.push(20);
deque.push(30);

deque.unshift(10);

console.log(deque);
// [10, 20, 30]

deque.push(40);

console.log(deque);
// [10, 20, 30, 40]

console.log(deque.shift()); // 10
console.log(deque.pop());   // 40

console.log(deque);
// [20, 30]
    
function isPalindrome(text) {
  const deque = text
    .toLowerCase()
    .replace(/[^a-z0-9]/g, "")
    .split("");

  while (deque.length > 1) {
    const first = deque.shift();
    const last = deque.pop();

    if (first !== last) {
      return false;
    }
  }

  return true;
}

console.log(isPalindrome("level")); // true
console.log(isPalindrome("hello")); // false
    

Pro Tip

When a problem requires removing elements from both ends, consider a deque before reaching for an array. In algorithmic problems, a deque is especially useful for sliding-window techniques where candidates can expire from the front while weaker candidates are removed from the rear.

🎨 IgKnight Docs

Master HTML, CSS, JS, Python, .NET and more with easy-to-follow lessons and practical code examples.

© 2025 IgKnight Learning Platform. All rights reserved.