Implementing a Deque Data Structure
This challenge asks you to implement a Deque (Double-Ended Queue) data structure in Python. Deques are versatile linear data structures that allow efficient insertion and deletion of elements from both the front and the rear. This is a fundamental data structure with applications in areas like breadth-first search algorithms, undo/redo functionality, and efficient queue/stack implementations.
Problem Description
Your task is to create a Python class that simulates the behavior of a Deque. The Deque should support the following core operations:
__init__(self): Initializes an empty Deque.is_empty(self): ReturnsTrueif the Deque is empty,Falseotherwise.size(self): Returns the number of elements currently in the Deque.add_front(self, item): Adds anitemto the front of the Deque.add_rear(self, item): Adds anitemto the rear of the Deque.remove_front(self): Removes and returns the item from the front of the Deque. Raises anIndexErrorif the Deque is empty.remove_rear(self): Removes and returns the item from the rear of the Deque. Raises anIndexErrorif the Deque is empty.peek_front(self): Returns the item at the front of the Deque without removing it. Raises anIndexErrorif the Deque is empty.peek_rear(self): Returns the item at the rear of the Deque without removing it. Raises anIndexErrorif the Deque is empty.
You should aim for efficient implementations of these operations, ideally with O(1) time complexity for all of them, assuming the underlying storage mechanism supports it.
Examples
Example 1:
# Initialize a deque
my_deque = Deque()
# Add elements
my_deque.add_rear(10)
my_deque.add_rear(20)
my_deque.add_front(5)
# Current state: [5, 10, 20]
print(my_deque.size()) # Output: 3
print(my_deque.peek_front()) # Output: 5
print(my_deque.peek_rear()) # Output: 20
# Remove elements
print(my_deque.remove_front()) # Output: 5
print(my_deque.remove_rear()) # Output: 20
# Current state: [10]
print(my_deque.size()) # Output: 1
print(my_deque.is_empty()) # Output: False
Explanation: This example demonstrates basic insertion and removal from both ends, along with checking size and emptiness.
Example 2:
# Initialize a deque
my_deque = Deque()
# Attempt to remove from an empty deque
try:
my_deque.remove_front()
except IndexError as e:
print(e) # Output: Cannot remove from an empty deque
try:
my_deque.peek_rear()
except IndexError as e:
print(e) # Output: Cannot peek at an empty deque
# Add and then remove all elements
my_deque.add_front('A')
my_deque.add_front('B')
my_deque.remove_rear() # Removes 'A'
my_deque.remove_front() # Removes 'B'
print(my_deque.is_empty()) # Output: True
Explanation: This example shows the expected behavior when trying to perform operations on an empty deque, and also tests sequences of additions and removals that empty the deque.
Example 3:
# Initialize a deque
my_deque = Deque()
# Add multiple items of different types
my_deque.add_rear(1)
my_deque.add_front("hello")
my_deque.add_rear(True)
my_deque.add_front([1, 2])
# Current state: [[1, 2], 'hello', 1, True]
print(my_deque.size()) # Output: 4
print(my_deque.remove_front()) # Output: [1, 2]
print(my_deque.remove_rear()) # Output: True
print(my_deque.remove_front()) # Output: 'hello'
print(my_deque.remove_front()) # Output: 1
# Current state: []
print(my_deque.is_empty()) # Output: True
Explanation: This example demonstrates that the deque can hold elements of various data types and tests a more complex sequence of operations.
Constraints
- The Deque should be able to store any Python object.
- The
remove_front,remove_rear,peek_front, andpeek_rearmethods must raise anIndexErrorwith an appropriate message (e.g., "Cannot remove from an empty deque") when called on an empty Deque. - Your implementation should aim for an average time complexity of O(1) for all provided operations.
Notes
- Consider using Python's built-in
collections.dequeas inspiration for the functionality, but you should implement your own version from scratch. - Think about the underlying data structure you will use to implement the Deque. A standard Python list can work, but be mindful of its performance characteristics for front insertions/deletions. Alternatively, you might consider a linked list approach.
- For the error messages in
IndexError, make them informative for debugging.