Implementing Stack, Queue, and Deque Data Structures
This document provides implementations of three fundamental data structures: Stack, Queue, and Deque. Each data structure is presented with code examples in both Python and C++, demonstrating their core functionalities using linked lists.
Stack Data Structure (LIFO)
A Stack is a Last-In, First-Out (LIFO) data structure. Elements are added and removed from the same end, often referred to as the “top” of the stack.
Python Stack Implementation
The Python implementation uses a simple Node class to build a linked list-based stack.
# Python Node Class for Linked List
class Node:
def __init__(self, data):
self.data = data
self.next = None
def __str__(self):
return str(self.data)
# Python Stack Class
class Stack:
def __init__(self):
self.head = None
def push(self, data): # Adds a new element to the top
new = Node(data)
if self.isEmpty():
self.head = new
else:
new.next = self.head
self.head = new
def pop(self): # Returns and removes the top element
if not self.isEmpty():
value_to_remove = self.head.data
self.head = self.head.next
return value_to_remove
else:
print('Stack is empty!') # Consider raising an exception in production code
def top(self): # Returns the top element's value (PEEK)
if not self.isEmpty():
return self.head.data
def isEmpty(self):
return self.head is None
def __str__(self):
result = '[ '
current = self.head
while current is not None:
result += current.__str__() + " "
current = current.next
result += ']'
return result
C++ Stack Implementation
The C++ implementation uses templates for generic types and manages memory manually.
// C++ Node Class for Linked List
template <typename T>
class Node {
public:
T value;
Node* next;
Node(T val) : value(val), next(nullptr) {}
// Overload stream insertion operator for Node
friend std::ostream& operator<<(std::ostream& output, const Node& n) {
output << n.value;
return output;
}
};
// C++ Stack Class
template <typename T>
class Stack {
private:
Node<T>* head;
public:
Stack() : head(nullptr) {} // Constructor
~Stack() { // Destructor to free memory
Node<T>* current = head;
while (current != nullptr) {
Node<T>* next_node = current->next;
delete current;
current = next_node;
}
head = nullptr; // Ensure head is null after destruction
}
void push(T value) { // Adds a new element to the top
Node<T>* new_node = new Node<T>(value);
if (isEmpty()) {
head = new_node;
} else {
new_node->next = head;
head = new_node;
}
}
T pop() { // Returns and removes the top element
if (!isEmpty()) {
Node<T>* node_to_delete = head;
T value_to_return = node_to_delete->value;
head = head->next;
delete node_to_delete;
return value_to_return;
}
// For non-pointer types, returning NULL might be problematic.
// Consider throwing an exception or using std::optional<T> in modern C++.
return (T) NULL;
}
T top() { // Returns the top element's value (PEEK)
if (!isEmpty()) {
return head->value;
}
return (T) NULL;
}
bool isEmpty() {
return head == nullptr;
}
// Overload stream insertion operator for Stack
friend std::ostream& operator<<(std::ostream& output, const Stack<T>& s) {
output << "[";
if (s.head != nullptr) {
Node<T>* current = s.head;
while (current != nullptr) {
output << (*current);
if (current->next != nullptr) output << ",";
current = current->next;
}
}
output << "]";
return output;
}
};
Queue Data Structure (FIFO)
A Queue is a First-In, First-Out (FIFO) data structure. Elements are added to one end (the “tail” or “back”) and removed from the other end (the “head” or “front”).
Python Queue Implementation
The Python implementation uses a simple Node class and maintains both head and tail pointers.
# Python Node Class for Linked List
class Node:
def __init__(self, data):
self.data = data
self.next = None
def __str__(self):
return str(self.data)
# Python Queue Class
class Queue:
def __init__(self):
self.head = None
self.tail = None
def push(self, data): # Adds a new element to the back (tail)
new = Node(data)
if self.isEmpty():
self.head = new
self.tail = new
else:
self.tail.next = new
self.tail = new
def pop(self): # Returns and removes the front element
if not self.isEmpty():
value_to_remove = self.head.data
self.head = self.head.next
if self.head is None: # If the last element was removed
self.tail = None
return value_to_remove
else:
print('Queue is empty!') # Consider raising an exception in production code
def top(self): # Returns the front element's value (PEEK)
if not self.isEmpty():
return self.head.data
def isEmpty(self):
return self.head is None and self.tail is None
def __str__(self):
result = '[ '
current = self.head
while current is not None:
result += current.__str__() + " "
current = current.next
result += ']'
return result
C++ Queue Implementation
The C++ implementation uses templates and manages memory for a linked list-based queue.
// C++ Node Class for Linked List
template <typename T>
class Node {
public:
T value;
Node* next;
Node(T val) : value(val), next(nullptr) {}
friend std::ostream& operator<<(std::ostream& output, const Node& n) {
output << n.value;
return output;
}
};
// C++ Queue Class
template <typename T>
class Queue {
private:
Node<T>* head;
Node<T>* tail;
public:
Queue() : head(nullptr), tail(nullptr) {} // Constructor
~Queue() { // Destructor to free memory
Node<T>* current = head;
while (current != nullptr) {
Node<T>* next_node = current->next;
delete current;
current = next_node;
}
head = nullptr;
tail = nullptr;
}
void push(T value) { // Adds a new element to the back (tail)
Node<T>* new_node = new Node<T>(value);
if (isEmpty()) {
head = new_node;
tail = new_node;
} else {
tail->next = new_node;
tail = new_node;
}
}
T pop() { // Returns and removes the front element
if (!isEmpty()) {
Node<T>* node_to_delete = head;
T value_to_return = node_to_delete->value;
head = head->next;
if (head == nullptr) { // If the last element was removed
tail = nullptr;
}
delete node_to_delete;
return value_to_return;
} else {
return (T) NULL;
}
}
T top() { // Returns the front element's value (PEEK)
if (!isEmpty()) {
return head->value;
}
return (T) NULL;
}
bool isEmpty() {
return head == nullptr; // If head is null, tail must also be null for an empty queue
}
friend std::ostream& operator<<(std::ostream& output, const Queue<T>& s) {
output << "[";
if (s.head != nullptr) {
Node<T>* current = s.head;
while (current != nullptr) {
output << (*current);
if (current->next != nullptr) output << ",";
current = current->next;
}
}
output << "]";
return output;
}
};
Deque Data Structure (Double-Ended Queue)
A Deque (Double-Ended Queue) is a data structure that allows elements to be added or removed from both the front and the back.
Python Deque Implementation
The Python implementation uses a Node class with both next
and prev
pointers for a doubly linked list.
# Python Node Class for Doubly Linked List
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
def __str__(self):
return str(self.data)
# Python Deque Class
class Deque:
def __init__(self):
self.head = None
self.tail = None
def push_front(self, data): # Adds a new element to the front
new = Node(data)
if self.isEmpty():
self.head = new
self.tail = new
else:
new.next = self.head
self.head.prev = new
self.head = new
def push_back(self, data): # Adds a new element to the back (tail)
new = Node(data)
if self.isEmpty():
self.head = new
self.tail = new
else:
self.tail.next = new
new.prev = self.tail
self.tail = new
def pop_front(self): # Returns and removes the front element
if not self.isEmpty():
value_to_remove = self.head.data
self.head = self.head.next
if self.head is None:
self.tail = None
else:
self.head.prev = None
return value_to_remove
else:
print('Deque is empty!') # Consider raising an exception in production code
def pop_back(self): # Returns and removes the back element
if not self.isEmpty():
value_to_remove = self.tail.data
self.tail = self.tail.prev
if self.tail is None:
self.head = None
else:
self.tail.next = None
return value_to_remove
else:
print('Deque is empty!') # Consider raising an exception in production code
def front(self): # Returns the front element's value (PEEK)
if not self.isEmpty():
return self.head.data
def back(self): # Returns the back element's value (PEEK)
if not self.isEmpty():
return self.tail.data
def isEmpty(self):
return self.head is None and self.tail is None
def __str__(self):
result = '[ '
current = self.head
while current is not None:
result += current.__str__() + " "
current = current.next
result += ']'
return result
C++ Deque Implementation
The C++ implementation uses templates and manages memory for a doubly linked list-based deque.
// C++ Node Class for Doubly Linked List
template <typename T>
class Node {
public:
T value;
Node* next;
Node* prev;
Node(T val) : value(val), next(nullptr), prev(nullptr) {}
friend std::ostream& operator<<(std::ostream& output, const Node& n) {
output << n.value;
return output;
}
};
// C++ Deque Class
template <typename T>
class Deque {
private:
Node<T>* head;
Node<T>* tail;
public:
Deque() : head(nullptr), tail(nullptr) {} // Constructor
~Deque() { // Destructor to free memory
Node<T>* current = head;
while (current != nullptr) {
Node<T>* next_node = current->next;
delete current;
current = next_node;
}
head = nullptr;
tail = nullptr;
}
void push_front(T value) { // Adds a new element to the front
Node<T>* new_node = new Node<T>(value);
if (isEmpty()) {
head = new_node;
tail = new_node;
} else {
new_node->next = head;
head->prev = new_node;
head = new_node;
}
}
void push_back(T value) { // Adds a new element to the back (tail)
Node<T>* new_node = new Node<T>(value);
if (isEmpty()) {
head = new_node;
tail = new_node;
} else {
tail->next = new_node;
new_node->prev = tail;
tail = new_node;
}
}
T pop_front() { // Returns and removes the front element
if (!isEmpty()) {
Node<T>* node_to_delete = head;
T value_to_return = node_to_delete->value;
head = head->next;
if (head == nullptr) {
tail = nullptr;
} else {
head->prev = nullptr;
}
delete node_to_delete;
return value_to_return;
} else {
return (T) NULL;
}
}
T pop_back() { // Returns and removes the back element
if (!isEmpty()) {
Node<T>* node_to_delete = tail;
T value_to_return = node_to_delete->value;
tail = tail->prev;
if (tail == nullptr) {
head = nullptr;
}
delete node_to_delete;
return value_to_return;
} else {
return (T) NULL;
}
}
T front() { // Returns the front element's value (PEEK)
if (!isEmpty()) {
return head->value;
}
return (T) NULL;
}
T back() { // Returns the back element's value (PEEK)
if (!isEmpty()) {
return tail->value;
}
return (T) NULL;
}
bool isEmpty() {
return head == nullptr && tail == nullptr;
}
friend std::ostream& operator<<(std::ostream& output, const Deque<T>& s) {
output << "[";
if (s.head != nullptr) {
Node<T>* current = s.head;
while (current != nullptr) {
output << (*current);
if (current->next != nullptr) output << ",";
current = current->next;
}
}
output << "]";
return output;
}
};