Table of contents
1.
Introduction
2.
What is Deque?
3.
Types of Deque
3.1.
1. ArrayDeque
3.2.
2. LinkedList
4.
Applications of Deque
5.
Operations on Deque
5.1.
1. Adding Elements
5.2.
2. Removing Elements
5.3.
3. Accessing Elements
5.4.
4. Other Operations
6.
Most Useful Methods of Deque
7.
Deque Implementation
7.1.
C
7.2.
C++
7.3.
Java
7.4.
Python
7.5.
Output
8.
Frequently Asked Questions
8.1.
What are the two types of dequeue in data structure?
8.2.
What is the difference between double-ended queue and normal queue?
8.3.
What is double-ended queue and priority queue in data structure?
9.
Conclusion
Last Updated: Nov 10, 2024
Easy

Deque

Author Anant Dhakad
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

STL library comes in handy whether it's a coding contest or DSA interview of some product based company. In this blog, we will learn about Deque in C++ STL. Sequence containers with the ability to expand and contract on both ends are known as double-ended queues. They're similar to vectors, but they're more efficient for element insertion and deletion. Contiguous storage allocation, unlike vectors, may not be guaranteed in Deque.

Deque

What is Deque?

Double-ended Queues are an implementation of the double-ended queue data structure. A queue data structure supports only end-to-end insertion and front-to-back deletion. This is similar to how people are removed from the front of a line and added to the back in real life. Double-ended queues are a type of queue that allows for insertion and deletion operations at both ends.

The functions for Deque are the same as for vector, with the addition of push and pop operations for both the front and back of the container.

Types of Deque

In Java, a Deque (Double Ended Queue) allows elements to be added or removed from both ends. There are two main types of deques based on the implementation:

1. ArrayDeque

  • Description: ArrayDeque is a resizable array-based implementation of the Deque interface.
  • Features:
    • Supports constant time (O(1)) operations for adding and removing elements from both ends.
    • Does not allow null elements.
    • Faster than LinkedList for most operations since it doesn't involve additional memory overhead for node pointers.
  • Use case: Preferred for applications where quick access and modification of elements from both ends of the deque are required.

2. LinkedList

  • Description: LinkedList implements the Deque interface using a doubly-linked list, meaning each element has references to both its next and previous elements.
  • Features:
    • Supports constant time (O(1)) operations for adding or removing elements at both ends.
    • Can be less efficient than ArrayDeque because of extra memory usage for each element (pointers to next and previous nodes).
  • Use case: Ideal when frequent insertions and deletions occur in the middle of the collection, as it offers efficient operations for removing or adding elements from any position.

Applications of Deque

  • Palindrome Checking: Deques are useful in checking whether a sequence of characters is a palindrome because elements can be added or removed from both ends efficiently.
  • Sliding Window Problems: Deques can be used for problems where you need to find the maximum or minimum of a sliding window, especially in problems involving arrays or lists.
  • Task Scheduling: Used in scheduling tasks in a manner where tasks can be processed in a first-in-first-out (FIFO) or last-in-first-out (LIFO) manner.
  • Deque in Caching Systems: Used in LRU (Least Recently Used) cache implementation for managing cache with efficient access to the most recent and least recent items.
  • Undo/Redo Operations: In applications like text editors, deques are used to maintain the history of actions for implementing undo and redo functionality.
  • Browser History Navigation: Deques are used to track the forward and backward navigation history in web browsers.

Operations on Deque

The Deque interface in Java provides various operations to add, remove, and inspect elements from both ends. The main operations are as follows:

1. Adding Elements

  • addFirst(E e): Inserts the specified element at the front of the deque. Throws an exception if the operation fails.
  • addLast(E e): Inserts the specified element at the end of the deque. Throws an exception if the operation fails.
  • offerFirst(E e): Inserts the specified element at the front of the deque, returning true on success or false if the operation fails.
  • offerLast(E e): Inserts the specified element at the end of the deque, returning true on success or false if the operation fails.

2. Removing Elements

  • removeFirst(): Removes and returns the first element of the deque. Throws an exception if the deque is empty.
  • removeLast(): Removes and returns the last element of the deque. Throws an exception if the deque is empty.
  • pollFirst(): Removes and returns the first element of the deque, or null if the deque is empty.
  • pollLast(): Removes and returns the last element of the deque, or null if the deque is empty.

3. Accessing Elements

  • getFirst(): Returns the first element without removing it. Throws an exception if the deque is empty.
  • getLast(): Returns the last element without removing it. Throws an exception if the deque is empty.
  • peekFirst(): Returns the first element without removing it, or null if the deque is empty.
  • peekLast(): Returns the last element without removing it, or null if the deque is empty.

4. Other Operations

  • size(): Returns the number of elements in the deque.
  • isEmpty(): Returns true if the deque is empty, otherwise false.
  • clear(): Removes all elements from the deque.
  • contains(Object o): Returns true if the deque contains the specified element.
  • toArray(): Returns an array containing all the elements in the deque in proper sequence.

These operations provide flexibility in using the deque data structure to solve a variety of problems requiring access to both ends of the collection.

Most Useful Methods of Deque

MethodDescriptionExample
push_back(x)Adds an element x to the right (back) end of the deque.dq.push_back(10);
push_front(x)Adds an element x to the left (front) end of the deque.dq.push_front(5);
pop_back()Removes the element from the right (back) end of the deque. If the deque is empty, results in undefined behavior.dq.pop_back();
pop_front()Removes the element from the left (front) end of the deque. If the deque is empty, results in undefined behavior.dq.pop_front();
front()Returns a reference to the element at the front of the deque.int frontElement = dq.front();
back()Returns a reference to the element at the back of the deque.int backElement = dq.back();
at(index)Returns a reference to the element at the given index. If the index is out of bounds, throws std::out_of_range.int element = dq.at(2);
operator[]Provides direct access to the element at the given index without bounds checking.int element = dq[2];
empty()Returns true if the deque is empty, otherwise false.bool isEmpty = dq.empty();
size()Returns the number of elements in the deque.size_t size = dq.size();
clear()Removes all elements from the deque, making it empty.dq.clear();
erase(start, end)Removes elements in the range [start, end) from the deque.dq.erase(dq.begin(), dq.begin() + 3);
insert(position, x)Inserts an element x at the given position in the deque.dq.insert(dq.begin() + 2, 8);
resize(n)Resizes the deque to contain n elements. If n is smaller, elements are removed; if larger, elements are added (default-initialized).dq.resize(5);
swap(other_deque)Swaps the contents of the current deque with other_deque.dq.swap(otherDeque);
begin()Returns an iterator to the first element of the deque.auto it = dq.begin();
end()Returns an iterator to one past the last element of the deque.auto it = dq.end();
rbegin()Returns a reverse iterator to the last element of the deque.auto it = dq.rbegin();
rend()Returns a reverse iterator to one before the first element of the deque.auto it = dq.rend();

Deque Implementation

Below is a C++ program that shows the usage of the most commonly used Deque methods.

  • C
  • C++
  • Java
  • Python

C

#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

// Struct representing a deque
typedef struct {
int arr[MAX_SIZE];
int front;
int rear;
} Deque;

// Function to initialize the deque
void initDeque(Deque* dq) {
dq->front = -1;
dq->rear = -1;
}

// Function to check if deque is empty
int isEmpty(Deque* dq) {
return (dq->front == -1);
}

// Function to check if deque is full
int isFull(Deque* dq) {
return (dq->rear == MAX_SIZE - 1);
}

// Function to add element at the back
void push_back(Deque* dq, int val) {
if (isFull(dq)) {
printf("Deque is full\n");
return;
}
if (dq->front == -1) {
dq->front = 0;
}
dq->arr[++(dq->rear)] = val;
}

// Function to add element at the front
void push_front(Deque* dq, int val) {
if (isFull(dq)) {
printf("Deque is full\n");
return;
}
if (dq->front == -1) {
dq->front = 0;
dq->rear = 0;
} else {
for (int i = dq->rear; i >= dq->front; i--) {
dq->arr[i + 1] = dq->arr[i];
}
dq->arr[dq->front] = val;
dq->rear++;
}
}

// Function to remove element from the front
void pop_front(Deque* dq) {
if (isEmpty(dq)) {
printf("Deque is empty\n");
return;
}
for (int i = dq->front; i < dq->rear; i++) {
dq->arr[i] = dq->arr[i + 1];
}
dq->rear--;
if (dq->rear == -1) {
dq->front = -1;
}
}

// Function to remove element from the back
void pop_back(Deque* dq) {
if (isEmpty(dq)) {
printf("Deque is empty\n");
return;
}
dq->rear--;
if (dq->rear == -1) {
dq->front = -1;
}
}

// Function to print elements of the deque
void printDequeElements(Deque* dq) {
for (int i = dq->front; i <= dq->rear; i++) {
printf("\t%d", dq->arr[i]);
}
printf("\n");
}

int main() {
Deque dq;
initDeque(&dq);

// Using assign equivalent by adding elements to deque
int a[] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
push_back(&dq, a[i]);
}

printf("after using assign: ");
printDequeElements(&dq);

// Clearing the deque
dq.front = dq.rear = -1;

// Size of deque
printf("size of deque: %d\n", (dq.front == -1) ? 0 : (dq.rear - dq.front + 1));

// Using push_back and push_front
push_back(&dq, 6);
push_front(&dq, 9);
push_back(&dq, 23);
push_front(&dq, 8);

printf("The deque dq is: ");
printDequeElements(&dq);

printf("dq.size(): %d\n", (dq.front == -1) ? 0 : (dq.rear - dq.front + 1));
printf("dq.front(): %d\n", dq.arr[dq.front]);
printf("dq.back(): %d\n", dq.arr[dq.rear]);

// Removing from front and back
printf("dq.pop_front(): ");
pop_front(&dq);
printDequeElements(&dq);

printf("dq.pop_back(): ");
pop_back(&dq);
printDequeElements(&dq);

return 0;
}
You can also try this code with Online C Compiler
Run Code

C++

#include <bits/stdc++.h>

// C++ header which contains implementation of deque.
#include <deque>

using namespace std;

void printDequeElements(deque<int> g){
   deque<int>::iterator it;
   for (it = g.begin(); it != g.end(); ++it)
       cout << '\t' << *it;
   cout << '\n';
}

int main(){
   // Declaring a empty deque.
   deque<int>dq;

   // deque::assign()
   vector<int>a = {1, 2, 3, 4, 5};
   dq.assign(a.begin(), a.end());

   cout << "after using assign: ";
   printDequeElements(dq);

   // deque::clear()
   dq.clear();

   // deque::size();
   cout << "size of deque: " << dq.size() << endl;

   // deque::push_back() && deque::push_front()
   dq.push_back(6);
   dq.push_front(9);
   dq.push_back(23);
   dq.push_front(8);
   cout << "The deque dq is : ";
   printDequeElements(dq);

   cout << "dq.size() : " << dq.size() << endl;
   // deque::front()
   cout << "dq.front() : " << dq.front() << endl;
   // deque::back()
   cout << "dq.back() : " << dq.back() << endl;

   // deque::pop_front()
   cout << "dq.pop_front() : ";
   dq.pop_front();
   printDequeElements(dq);

   // deque::pop_back()
   cout << "dq.pop_back() : ";
   dq.pop_back();
   printDequeElements(dq);
}
You can also try this code with Online C++ Compiler
Run Code

Java

import java.util.*;

public class Main {

// Method to print elements of the deque
public static void printDequeElements(Deque<Integer> dq) {
for (Integer elem : dq) {
System.out.print("\t" + elem);
}
System.out.println();
}

public static void main(String[] args) {
// Declaring an empty deque
Deque<Integer> dq = new ArrayDeque<>();

// Using assign equivalent by adding elements to deque
Integer[] a = {1, 2, 3, 4, 5};
dq.addAll(Arrays.asList(a));

System.out.print("after using assign: ");
printDequeElements(dq);

// Clearing the deque
dq.clear();

// Size of deque
System.out.println("size of deque: " + dq.size());

// Using push_back (add) and push_front (addFirst)
dq.addLast(6);
dq.addFirst(9);
dq.addLast(23);
dq.addFirst(8);

System.out.print("The deque dq is: ");
printDequeElements(dq);

System.out.println("dq.size(): " + dq.size());
System.out.println("dq.front(): " + dq.peekFirst());
System.out.println("dq.back(): " + dq.peekLast());

// Removing from front and back
System.out.print("dq.pop_front(): ");
dq.pollFirst();
printDequeElements(dq);

System.out.print("dq.pop_back(): ");
dq.pollLast();
printDequeElements(dq);
}
}
You can also try this code with Online Java Compiler
Run Code

Python

from collections import deque

# Function to print elements of deque
def print_deque_elements(dq):
for elem in dq:
print(f'\t{elem}', end="")
print()

# Main function
def main():
# Declaring an empty deque
dq = deque()

# Using assign equivalent by adding elements to deque
a = [1, 2, 3, 4, 5]
dq.extend(a)

print("after using assign: ", end="")
print_deque_elements(dq)

# Clearing the deque
dq.clear()

# Size of deque
print("size of deque:", len(dq))

# Using push_back (append) and push_front (appendleft)
dq.append(6)
dq.appendleft(9)
dq.append(23)
dq.appendleft(8)

print("The deque dq is:", end=" ")
print_deque_elements(dq)

print(f"dq.size(): {len(dq)}")
print(f"dq.front(): {dq[0]}")
print(f"dq.back(): {dq[-1]}")

# Removing from front and back
print("dq.pop_front():", end=" ")
dq.popleft()
print_deque_elements(dq)

print("dq.pop_back():", end=" ")
dq.pop()
print_deque_elements(dq)

if __name__ == "__main__":
main()
You can also try this code with Online Python Compiler
Run Code

Output


after using assign:     1       2       3       4       5
size of deque: 0
The deque dq is :       8       9       6       23
dq.size() : 4
dq.front() : 8
dq.back() : 23
dq.pop_front() :        9       6       23
dq.pop_back() :         9       6

Frequently Asked Questions

What are the two types of dequeue in data structure?

In data structures, the two types of deque are:

  1. Input-restricted deque: Elements can only be added from one end (either front or back), but can be removed from both ends.
  2. Output-restricted deque: Elements can be added from both ends but removed only from one end.

What is the difference between double-ended queue and normal queue?

A normal queue follows FIFO (First In First Out) and allows operations at only one end (enqueuing at the rear and dequeuing from the front). A double-ended queue (deque) allows operations at both ends for adding and removing elements.

What is double-ended queue and priority queue in data structure?

A double-ended queue (deque) allows adding/removing elements from both ends. A priority queue stores elements in order of priority, with higher-priority elements dequeued before lower-priority ones, typically implemented using a heap data structure.

Conclusion

In this article, we learnt about Deque in C++. A deque (double-ended queue) is a powerful and versatile data structure that allows efficient addition and removal of elements from both ends. Its flexibility makes it suitable for various applications like task scheduling, sliding windows, and buffer management. Compared to other data structures like stacks and queues, deques provide more functionality, allowing both ends to be manipulated efficiently. 

We hope that this blog has helped you enhance your knowledge regarding Deque in C++ and if you would like to learn more, check out our articles on the platform Code360. Also, do check out our course on C++

Live masterclass