codingstuff.io
ExploreTutorialsProblemsCS Subjects
Get Started
ExploreTutorialsProblemsCS Subjects
Get Started
codingstuff.io

Master the art of building software through interactive tutorials, real-world problems, and guided projects.

Pune, Maharashtra, India

codingstuffmail@gmail.com

Product

  • Explore
  • Tutorials
  • Problems
  • CS Subjects

Company

  • About
  • Contact
  • Privacy Policy
  • Terms & Conditions
  • Sitemap

© 2026 codingstuff.io. All rights reserved.

Built with ❤️ for developers everywhere

/
/
All Tutorials
🧮

Data Structures & Algorithms

9 / 65 topics
7Stacks8Queues9Deques (Double-Ended Queues)10Priority Queues
Tutorials/Data Structures & Algorithms/Deques (Double-Ended Queues)
🧮Data Structures & Algorithms

Deques (Double-Ended Queues)

Updated 2026-04-20
2 min read

Deques (Double-Ended Queues)

Introduction

A deque, short for double-ended queue, is a versatile data structure that allows efficient insertion and deletion operations at both ends. Unlike a standard queue, which only supports operations from one end, a deque provides flexibility in managing elements from both the front and back. This makes deques particularly useful in scenarios where you need to perform frequent insertions and deletions on either side of the collection.

Key Characteristics

  • Double-ended: Supports insertion and deletion at both ends.
  • Dynamic Size: Can grow or shrink as needed.
  • Efficient Operations: Provides constant time complexity for operations like push, pop, peek from front and back.

Use Cases

  1. Sliding Window Algorithms: Useful in scenarios where you need to maintain a fixed-size window over data, such as finding the maximum element in a sliding window of an array.
  2. Implementing Stacks and Queues: Deques can be used to implement both stacks (LIFO) and queues (FIFO) with efficient operations.
  3. Breadth-First Search (BFS): In BFS, deques are often used to manage the queue of nodes to explore next, allowing for efficient addition and removal of nodes.

Implementation

Array-Based Deque

One common way to implement a deque is using an array that dynamically resizes as needed. This approach provides constant time complexity for most operations but may require additional overhead for resizing.

class ArrayDeque {
  constructor() {
    this.items = [];
  }

  addFront(element) {
    this.items.unshift(element);
  }

  removeFront() {
    if (this.isEmpty()) return undefined;
    return this.items.shift();
  }

  peekFront() {
    if (this.isEmpty()) return undefined;
    return this.items[0];
  }

  addBack(element) {
    this.items.push(element);
  }

  removeBack() {
    if (this.isEmpty()) return undefined;
    return this.items.pop();
  }

  peekBack() {
    if (this.isEmpty()) return undefined;
    return this.items[this.items.length - 1];
  }

  isEmpty() {
    return this.items.length === 0;
  }

  size() {
    return this.items.length;
  }
}

Linked List-Based Deque

Another efficient way to implement a deque is using a doubly linked list. This approach ensures that all operations are performed in constant time without the overhead of resizing.

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

class LinkedListDeque {
  constructor() {
    this.front = null;
    this.back = null;
    this.size = 0;
  }

  addFront(element) {
    const newNode = new Node(element);
    if (this.isEmpty()) {
      this.front = newNode;
      this.back = newNode;
    } else {
      newNode.next = this.front;
      this.front.prev = newNode;
      this.front = newNode;
    }
    this.size++;
  }

  removeFront() {
    if (this.isEmpty()) return undefined;
    const element = this.front.element;
    this.front = this.front.next;
    if (this.front) this.front.prev = null;
    else this.back = null;
    this.size--;
    return element;
  }

  peekFront() {
    if (this.isEmpty()) return undefined;
    return this.front.element;
  }

  addBack(element) {
    const newNode = new Node(element);
    if (this.isEmpty()) {
      this.front = newNode;
      this.back = newNode;
    } else {
      newNode.prev = this.back;
      this.back.next = newNode;
      this.back = newNode;
    }
    this.size++;
  }

  removeBack() {
    if (this.isEmpty()) return undefined;
    const element = this.back.element;
    this.back = this.back.prev;
    if (this.back) this.back.next = null;
    else this.front = null;
    this.size--;
    return element;
  }

  peekBack() {
    if (this.isEmpty()) return undefined;
    return this.back.element;
  }

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

  size() {
    return this.size;
  }
}

Best Practices

  1. Choose the Right Implementation: Depending on your use case, choose between array-based and linked list-based deques. Array-based deques are simpler but may have overhead for resizing, while linked list-based deques offer constant time operations without resizing.
  2. Handle Edge Cases: Always check for edge cases such as empty deques before performing operations that modify the deque.
  3. Optimize Memory Usage: In array-based implementations, consider optimizing memory usage by avoiding unnecessary resizing or using a more sophisticated resizing strategy.

Conclusion

Deques are powerful data structures that offer flexibility and efficiency in managing collections of elements from both ends. By understanding their characteristics, use cases, and implementation details, you can leverage deques to solve complex problems in various domains. Whether you're implementing algorithms, designing systems, or working on software projects, mastering deques will undoubtedly enhance your ability to handle data efficiently.


PreviousQueuesNext Priority Queues

Recommended Gear

QueuesPriority Queues