
Problem Statement
The task is to design a circular double-ended queue (deque) using a class called MyCircularDeque
. This custom data structure allows you to:
- Initialize the deque with a specified maximum size using the constructor
MyCircularDeque(int k)
. - Add an item at the front of the deque with
insertFront()
. - Add an item at the rear of the deque with
insertLast()
. - Remove an item from the front of the deque with
deleteFront()
. - Remove an item from the rear of the deque with
deleteLast()
. - Retrieve the item at the front of the deque with
getFront()
, which returns-1
if the deque is empty. - Retrieve the item at the rear of the deque with
getRear()
, which also returns-1
if the deque is empty. - Check if the deque is empty using
isEmpty()
, which returnstrue
if it is empty andfalse
otherwise. - Check if the deque is full using
isFull()
, which returnstrue
if it is full andfalse
otherwise.
This implementation must handle up to 2000 operations efficiently and maintain all properties typical to a deque, with the added complexity that it is circular, meaning the ends are conceptually connected.
Examples
Example 1
Input:
["MyCircularDeque", "insertLast", "insertLast", "insertFront", "insertFront", "getRear", "isFull", "deleteLast", "insertFront", "getFront"] [[3], [1], [2], [3], [4], [], [], [], [4], []]
Output:
[null, true, true, true, false, 2, true, true, true, 4]
Explanation:
MyCircularDeque myCircularDeque = new MyCircularDeque(3); myCircularDeque.insertLast(1); // return True myCircularDeque.insertLast(2); // return True myCircularDeque.insertFront(3); // return True myCircularDeque.insertFront(4); // return False, the queue is full. myCircularDeque.getRear(); // return 2 myCircularDeque.isFull(); // return True myCircularDeque.deleteLast(); // return True myCircularDeque.insertFront(4); // return True myCircularDeque.getFront(); // return 4
Constraints
1 <= k <= 1000
0 <= value <= 1000
- At most
2000
calls will be made toinsertFront
,insertLast
,deleteFront
,deleteLast
,getFront
,getRear
,isEmpty
,isFull
.
Approach and Intuition
Data Structure Setup:
- The fundamental structure will likely consist of an array to simulate the deque since direct access to the front and rear elements is crucial.
- Two pointers or indices (for the front and rear) will help manage where the next elements will be inserted or removed.
Method Implementations:
insertFront
andinsertLast
need boundary checks to see if the deque is full before inserting a new item.deleteFront
anddeleteLast
should check if the deque is empty before attempting to remove an item.- For the
isFull
method, a comparison of the number of items in the deque and its maximum capacity will be necessary. - The
isEmpty
method simply checks if the front is in the same position as the rear and that position holds no valid item. getFront
andgetRear
return values based on the front and rear pointers but handle cases where the deque might be empty.
Circularity Management:
- As operations may cause the front or rear indices to potentially "wrap around" the fixed-size array boundary, modular arithmetic can be used (
mod k
) during insertions and deletions to rotate back to the start or end of the array.
- As operations may cause the front or rear indices to potentially "wrap around" the fixed-size array boundary, modular arithmetic can be used (
Example Walkthrough:
- The initialization assigns an array of size
k
with all elements set to indicate inactive/deleted slots. - Insertions adjust the respective pointer, insert the value, and handle the wrapping if needed.
- Deletions make the slot inactive and adjust the pointer with modular adjustments for wrapping.
- Status checks directly derive from pointer positions and active counts.
- The initialization assigns an array of size
This implementation highlights a tailored approach to efficiently manage inserts and deletes from both ends of a dynamically adjusting circular structure. The operations are designed to run in constant time O(1), crucial for high-performance requirements where rapid access and modifications are frequent.
Solutions
- C++
- Java
- Python
class CircularDeque {
private:
vector<int> deque;
int head;
int tail;
int count;
int maxCapacity;
public:
CircularDeque(int k) {
deque = vector<int>(k);
count = 0;
maxCapacity = k;
head = 0;
tail = k - 1;
}
bool insertFront(int value) {
if (isFull()) return false;
head = (head - 1 + maxCapacity) % maxCapacity;
deque[head] = value;
count++;
return true;
}
bool insertLast(int value) {
if (isFull()) return false;
tail = (tail + 1) % maxCapacity;
deque[tail] = value;
count++;
return true;
}
bool deleteFront() {
if (isEmpty()) return false;
head = (head + 1) % maxCapacity;
count--;
return true;
}
bool deleteLast() {
if (isEmpty()) return false;
tail = (tail - 1 + maxCapacity) % maxCapacity;
count--;
return true;
}
int getFront() {
if (isEmpty()) return -1;
return deque[head];
}
int getRear() {
if (isEmpty()) return -1;
return deque[tail];
}
bool isEmpty() { return count == 0; }
bool isFull() { return count == maxCapacity; }
};
The provided C++ code implements a data structure known as a Circular Deque (Double Ended Queue). This implementation allows for efficient addition and removal of items from both the front and the rear ends. Here's an overview of the functionality provided by the CircularDeque
class:
Initialization: The constructor
CircularDeque(int k)
initializes the deque with a capacityk
. It sets up an internal vector to store the elements and maintains four properties:head
,tail
,count
, andmaxCapacity
to manage the queue's state.Insert Operations:
insertFront(int value)
: Inserts an item at the front of the deque. It checks if the deque is full usingisFull()
. If not full, it adjusts thehead
pointer and inserts the value.insertLast(int value)
: Appends an item to the rear of the deque. Similar toinsertFront
, it checks for available space and inserts the item if possible, updating thetail
pointer.
Delete Operations:
deleteFront()
: Removes an item from the front of the deque. It checks if the deque is empty usingisEmpty()
before removing the item and adjusting thehead
pointer.deleteLast()
: Eliminates an item from the rear. It similarly verifies non-emptiness and adjusts thetail
pointer.
Access Operations:
getFront()
: Retrieves the front item of the deque. If the deque is empty, it returns -1.getRear()
: Fetches the rear item. Again, if empty, it returns -1.
Utility Functions:
isEmpty()
: Checks if the deque is currently empty.isFull()
: Determines if the deque has reached its capacity.
This implementation leverages modular arithmetic to efficiently wrap around the internal vector indices, ensuring constant-time operations for insertions and deletions. This structure is particularly useful in situations where frequent additions and removals from both ends of a queue are necessary, providing flexibility and efficiency over traditional queues or stacks.
class CircularDeque {
int[] elements;
int head;
int tail;
int count;
int maxCapacity;
public CircularDeque(int k) {
elements = new int[k];
count = 0;
maxCapacity = k;
head = 0;
tail = k - 1;
}
public boolean insertFront(int value) {
if (isFull()) return false;
head = (head - 1 + maxCapacity) % maxCapacity;
elements[head] = value;
count++;
return true;
}
public boolean insertLast(int value) {
if (isFull()) return false;
tail = (tail + 1) % maxCapacity;
elements[tail] = value;
count++;
return true;
}
public boolean deleteFront() {
if (isEmpty()) return false;
head = (head + 1) % maxCapacity;
count--;
return true;
}
public boolean deleteLast() {
if (isEmpty()) return false;
tail = (tail - 1 + maxCapacity) % maxCapacity;
count--;
return true;
}
public int getFront() {
if (isEmpty()) return -1;
return elements[head];
}
public int getRear() {
if (isEmpty()) return -1;
return elements[tail];
}
public boolean isEmpty() {
return count == 0;
}
public boolean isFull() {
return count == maxCapacity;
}
}
This Java solution implements a Circular Deque (double-ended queue) using an array. Here's a detailed explanation of how this implementation works:
- Create a fixed-size array
elements
to store deque elements and integer variableshead
,tail
,count
, andmaxCapacity
to track the state of the deque. - Initialize the
CircularDeque
with a constructor specifying its maximum capacity, sethead
to 0, andtail
to maximum capacity minus one. Initially,count
, which tracks the number of elements in the deque, is set to 0. - Implement
insertFront(int value)
to add an element at the front. If the deque is full (determined byisFull()
), the operation returnsfalse
. Otherwise, adjusthead
and insert the value, then increment thecount
. - Implement
insertLast(int value)
to add an element at the end. This method also checks if the deque is full, adjusts thetail
, inserts the value, and increments thecount
. deleteFront()
removes an element from the front. If the deque is empty (checked byisEmpty()
), it returnsfalse
. If not, it adjustshead
and decrementscount
.deleteLast()
removes an element from the end, similarly working with checks for emptiness, adjusting thetail
, and modifyingcount
.getFront()
retrieves the element at the front. If empty, it returns-1
; otherwise, it returns the element at thehead
index.getRear()
retrieves the element at the end, operating similarly by checking emptiness and then accessing the element at thetail
index.isEmpty()
andisFull()
provide utility checks to see if the deque is empty or full, respectively, used in various other methods to determine operation feasibility.
This circular deque efficiently utilizes the circular nature of the underlying array, allowing both front and rear operations to wrap around the end of the array using modular arithmetic.
class CircularDeque:
def __init__(self, capacity):
self.data = [0] * capacity
self.head = 0
self.tail = capacity - 1
self.count = 0
self.max_size = capacity
def insertFront(self, value):
if self.isFull():
return False
self.head = (self.head - 1 + self.max_size) % self.max_size
self.data[self.head] = value
self.count += 1
return True
def insertLast(self, value):
if self.isFull():
return False
self.tail = (self.tail + 1) % self.max_size
self.data[self.tail] = value
self.count += 1
return True
def deleteFront(self):
if self.isEmpty():
return False
self.head = (self.head + 1) % self.max_size
self.count -= 1
return True
def deleteLast(self):
if self.isEmpty():
return False
self.tail = (self.tail - 1 + self.max_size) % self.max_size
self.count -= 1
return True
def getFront(self):
if self.isEmpty():
return -1
return self.data[self.head]
def getRear(self):
if self.isEmpty():
return -1
return self.data[self.tail]
def isEmpty(self):
return self.count == 0
def isFull(self):
return self.count == self.max_size
The solution involves implementing a data structure called CircularDeque
using Python. This structure allows operations to be performed from both ends of the deque, which are optimized by circular indexing to handle wrap-around at the ends.
Initialize the deque with a specified capacity:
- An array
data
is allocated with the size equal tocapacity
to store the elements of the deque. - Pointers
head
andtail
are initiated to point respectively to the beginning and the end of the deque. count
keeps track of the number of elements currently stored, andmax_size
holds the maximum number of items that can be accommodated.
- An array
Insert elements at both ends:
insertFront
: Adds an element at the front. If the deque is full, it returnsFalse
; otherwise, it adjusts thehead
pointer to make space and inserts the new item.insertLast
: Appends an element at the back. If there is no space, it returnsFalse
; otherwise, it moves thetail
pointer and adds the element.
Delete elements from both ends:
deleteFront
: Removes the front element. ReturnsFalse
if the deque is empty; otherwise, it forwards thehead
pointer and reduces the count.deleteLast
: Removes the back element. ReturnsFalse
if empty; otherwise, it moves thetail
pointer backward.
Access elements:
getFront
: Returns the front element or -1 if empty.getRear
: Provides the rear element or -1 if there is no element.
Check states:
isEmpty
: ReturnsTrue
if there are no elements in the deque, elseFalse
.isFull
: Indicates if the deque has reached its capacity.
By executing these methods, the CircularDeque structure efficiently manages its operations without needing to shift elements, utilizing the mod operation to maintain the circular references. This design ensures optimal performance for operations at both ends of the deque.
No comments yet.