Welcome to Subscribe On Youtube
Formatted question description: https://leetcode.ca/all/622.html
622. Design Circular Queue
Level
Medium
Description
Design your implementation of the circular queue. The circular queue is a linear data structure in which the operations are performed based on FIFO (First In First Out) principle and the last position is connected back to the first position to make a circle. It is also called “Ring Buffer”.
One of the benefits of the circular queue is that we can make use of the spaces in front of the queue. In a normal queue, once the queue becomes full, we cannot insert the next element even if there is a space in front of the queue. But using the circular queue, we can use the space to store new values.
Your implementation should support following operations:
MyCircularQueue(k)
: Constructor, set the size of the queue to be k.Front
: Get the front item from the queue. If the queue is empty, return -1.Rear
: Get the last item from the queue. If the queue is empty, return -1.enQueue(value)
: Insert an element into the circular queue. Return true if the operation is successful.deQueue()
: Delete an element from the circular queue. Return true if the operation is successful.isEmpty()
: Checks whether the circular queue is empty or not.isFull()
: Checks whether the circular queue is full or not.
Example:
MyCircularQueue circularQueue = new MyCircularQueue(3); // set the size to be 3
circularQueue.enQueue(1); // return true
circularQueue.enQueue(2); // return true
circularQueue.enQueue(3); // return true
circularQueue.enQueue(4); // return false, the queue is full
circularQueue.Rear(); // return 3
circularQueue.isFull(); // return true
circularQueue.deQueue(); // return true
circularQueue.enQueue(4); // return true
circularQueue.Rear(); // return 4
Note:
- All values will be in the range of [0, 1000].
- The number of operations will be in the range of [1, 1000].
- Please do not use the built-in Queue library.
Solution - one pointer
(2 pointers solution is obvious btw)
Attributes
self.q
: A list initialized with zeros, used to store the elements of the queue. Its size isk
.self.front
: An integer that points to the front of the queue.self.size
: The current number of elements in the queue.self.capacity
: The maximum number of elements the queue can hold, which is equal tok
.
Methods
__init__(self, k: int)
Initializes the circular queue with a given capacity k
.
enQueue(self, value: int) -> bool
Attempts to insert an element value
into the queue.
- If the queue is full (
self.isFull()
returnsTrue
), it returnsFalse
and does not add the element. - Otherwise, it calculates the index where the new element should be inserted, which is
(self.front + self.size) % self.capacity
, and inserts thevalue
there. It then incrementsself.size
and returnsTrue
.
deQueue(self) -> bool
Removes an element from the front of the queue.
- If the queue is empty (
self.isEmpty()
returnsTrue
), it returnsFalse
. - Otherwise, it updates
self.front
to the next position in the queue using(self.front + 1) % self.capacity
and decrementsself.size
. ReturnsTrue
.
Front(self) -> int
Returns the front element of the queue.
- If the queue is empty, returns
-1
. - Otherwise, returns the element at
self.front
.
Rear(self) -> int
Returns the last element of the queue.
- If the queue is empty, returns
-1
. - Otherwise, calculates the index of the rear element as
(self.front + self.size - 1) % self.capacity
and returns the element at that index.
isEmpty(self) -> bool
Checks if the queue is empty by comparing self.size
to 0
.
isFull(self) -> bool
Checks if the queue is full by comparing self.size
to self.capacity
.
How It Works
The circular queue uses modular arithmetic to wrap around the end of the queue back to the beginning, hence “circular”. The % self.capacity
ensures that any index calculation will always result in a valid index within the bounds of the list self.q
. This approach efficiently utilizes the allocated space for the queue by avoiding the need to shift elements, which would be required in a non-circular queue implementation when elements are removed from the front.
-
public class Design_Circular_Queue { class MyCircularQueue { int[] arr; int size; int capacity; int front; int back; /** * Initialize your data structure here. Set the size of the queue to be k. */ public MyCircularQueue(int k) { arr = new int[k]; capacity = k; size = 0; front = 0; back = -1; } /** * Insert an element into the circular queue. Return true if the operation is successful. */ public boolean enQueue(int value) { if (size == capacity) { return false; } ++back; arr[back % arr.length] = value; ++size; return true; } /** * Delete an element from the circular queue. Return true if the operation is successful. */ public boolean deQueue() { if (size == 0) return false; ++front; --size; return true; } /** * Get the front item from the queue. */ public int Front() { if (size == 0) return -1; return arr[front % arr.length]; } /** * Get the last item from the queue. */ public int Rear() { if (size == 0) return -1; return arr[back % arr.length]; } /** * Checks whether the circular queue is empty or not. */ public boolean isEmpty() { return size == 0; } /** * Checks whether the circular queue is full or not. */ public boolean isFull() { return size == capacity; } } } ############ class MyCircularQueue { private int[] q; private int front; private int size; private int capacity; public MyCircularQueue(int k) { q = new int[k]; capacity = k; } public boolean enQueue(int value) { if (isFull()) { return false; } int idx = (front + size) % capacity; q[idx] = value; ++size; return true; } public boolean deQueue() { if (isEmpty()) { return false; } front = (front + 1) % capacity; --size; return true; } public int Front() { if (isEmpty()) { return -1; } return q[front]; } public int Rear() { if (isEmpty()) { return -1; } int idx = (front + size - 1) % capacity; return q[idx]; } public boolean isEmpty() { return size == 0; } public boolean isFull() { return size == capacity; } } /** * Your MyCircularQueue object will be instantiated and called as such: * MyCircularQueue obj = new MyCircularQueue(k); * boolean param_1 = obj.enQueue(value); * boolean param_2 = obj.deQueue(); * int param_3 = obj.Front(); * int param_4 = obj.Rear(); * boolean param_5 = obj.isEmpty(); * boolean param_6 = obj.isFull(); */
-
// OJ: https://leetcode.com/problems/design-circular-queue/ // Time: O(1) for all // Space: O(K) class MyCircularQueue { private: vector<int> v; int start = 0, len = 0; public: MyCircularQueue(int k): v(k) {} bool enQueue(int value) { if (isFull()) return false; v[(start + len++) % v.size()] = value; return true; } bool deQueue() { if (isEmpty()) return false; start = (start + 1) % v.size(); --len; return true; } int Front() { if (isEmpty()) return -1; return v[start]; } int Rear() { if (isEmpty()) return -1; return v[(start + len - 1) % v.size()]; } bool isEmpty() { return !len; } bool isFull() { return len == v.size(); } };
-
class MyCircularQueue: def __init__(self, k: int): self.q = [0] * k self.front = 0 self.size = 0 self.capacity = k def enQueue(self, value: int) -> bool: if self.isFull(): return False idx = (self.front + self.size) % self.capacity self.q[idx] = value self.size += 1 return True def deQueue(self) -> bool: if self.isEmpty(): return False self.front = (self.front + 1) % self.capacity self.size -= 1 return True def Front(self) -> int: return -1 if self.isEmpty() else self.q[self.front] def Rear(self) -> int: if self.isEmpty(): return -1 idx = (self.front + self.size - 1) % self.capacity return self.q[idx] def isEmpty(self) -> bool: return self.size == 0 def isFull(self) -> bool: return self.size == self.capacity # Your MyCircularQueue object will be instantiated and called as such: # obj = MyCircularQueue(k) # param_1 = obj.enQueue(value) # param_2 = obj.deQueue() # param_3 = obj.Front() # param_4 = obj.Rear() # param_5 = obj.isEmpty() # param_6 = obj.isFull() ############ class MyCircularQueue(object): def __init__(self, k): """ Initialize your data structure here. Set the size of the queue to be k. :type k: int """ self.queue = [] self.size = k self.front = 0 self.rear = 0 def enQueue(self, value): """ Insert an element into the circular queue. Return true if the operation is successful. :type value: int :rtype: bool """ if self.rear - self.front < self.size: self.queue.append(value) self.rear += 1 return True else: return False def deQueue(self): """ Delete an element from the circular queue. Return true if the operation is successful. :rtype: bool """ if self.rear - self.front > 0: self.front += 1 return True else: return False def Front(self): """ Get the front item from the queue. :rtype: int """ if self.isEmpty(): return -1 else: return self.queue[self.front] def Rear(self): """ Get the last item from the queue. :rtype: int """ if self.isEmpty(): return -1 else: return self.queue[self.rear - 1] def isEmpty(self): """ Checks whether the circular queue is empty or not. :rtype: bool """ return self.front == self.rear def isFull(self): """ Checks whether the circular queue is full or not. :rtype: bool """ return self.rear - self.front == self.size # Your MyCircularQueue object will be instantiated and called as such: # obj = MyCircularQueue(k) # param_1 = obj.enQueue(value) # param_2 = obj.deQueue() # param_3 = obj.Front() # param_4 = obj.Rear() # param_5 = obj.isEmpty() # param_6 = obj.isFull()
-
type MyCircularQueue struct { front int size int capacity int q []int } func Constructor(k int) MyCircularQueue { q := make([]int, k) return MyCircularQueue{0, 0, k, q} } func (this *MyCircularQueue) EnQueue(value int) bool { if this.IsFull() { return false } idx := (this.front + this.size) % this.capacity this.q[idx] = value this.size++ return true } func (this *MyCircularQueue) DeQueue() bool { if this.IsEmpty() { return false } this.front = (this.front + 1) % this.capacity this.size-- return true } func (this *MyCircularQueue) Front() int { if this.IsEmpty() { return -1 } return this.q[this.front] } func (this *MyCircularQueue) Rear() int { if this.IsEmpty() { return -1 } idx := (this.front + this.size - 1) % this.capacity return this.q[idx] } func (this *MyCircularQueue) IsEmpty() bool { return this.size == 0 } func (this *MyCircularQueue) IsFull() bool { return this.size == this.capacity } /** * Your MyCircularQueue object will be instantiated and called as such: * obj := Constructor(k); * param_1 := obj.EnQueue(value); * param_2 := obj.DeQueue(); * param_3 := obj.Front(); * param_4 := obj.Rear(); * param_5 := obj.IsEmpty(); * param_6 := obj.IsFull(); */
-
class MyCircularQueue { private queue: number[]; private left: number; private right: number; private capacity: number; constructor(k: number) { this.queue = new Array(k); this.left = 0; this.right = 0; this.capacity = k; } enQueue(value: number): boolean { if (this.isFull()) { return false; } this.queue[this.right % this.capacity] = value; this.right++; return true; } deQueue(): boolean { if (this.isEmpty()) { return false; } this.left++; return true; } Front(): number { if (this.isEmpty()) { return -1; } return this.queue[this.left % this.capacity]; } Rear(): number { if (this.isEmpty()) { return -1; } return this.queue[(this.right - 1) % this.capacity]; } isEmpty(): boolean { return this.right - this.left === 0; } isFull(): boolean { return this.right - this.left === this.capacity; } } /** * Your MyCircularQueue object will be instantiated and called as such: * var obj = new MyCircularQueue(k) * var param_1 = obj.enQueue(value) * var param_2 = obj.deQueue() * var param_3 = obj.Front() * var param_4 = obj.Rear() * var param_5 = obj.isEmpty() * var param_6 = obj.isFull() */
-
struct MyCircularQueue { queue: Vec<i32>, left: usize, right: usize, capacity: usize, } /** * `&self` means the method takes an immutable reference. * If you need a mutable reference, change it to `&mut self` instead. */ impl MyCircularQueue { fn new(k: i32) -> Self { let k = k as usize; Self { queue: vec![0; k], left: 0, right: 0, capacity: k, } } fn en_queue(&mut self, value: i32) -> bool { if self.is_full() { return false; } self.queue[self.right % self.capacity] = value; self.right += 1; true } fn de_queue(&mut self) -> bool { if self.is_empty() { return false; } self.left += 1; true } fn front(&self) -> i32 { if self.is_empty() { return -1; } self.queue[self.left % self.capacity] } fn rear(&self) -> i32 { if self.is_empty() { return -1; } self.queue[(self.right - 1) % self.capacity] } fn is_empty(&self) -> bool { self.right - self.left == 0 } fn is_full(&self) -> bool { self.right - self.left == self.capacity } } /** * Your MyCircularQueue object will be instantiated and called as such: * let obj = MyCircularQueue::new(k); * let ret_1: bool = obj.en_queue(value); * let ret_2: bool = obj.de_queue(); * let ret_3: i32 = obj.front(); * let ret_4: i32 = obj.rear(); * let ret_5: bool = obj.is_empty(); * let ret_6: bool = obj.is_full(); */ */