Circular Queue Implementation Using Array in DSA Rust - Time & Space Complexity
We want to understand how the time taken by circular queue operations changes as the queue size grows.
Specifically, how fast can we add or remove items in this circular queue?
Analyze the time complexity of the following code snippet.
struct CircularQueue {
data: Vec<i32>,
front: usize,
rear: usize,
size: usize,
capacity: usize,
}
impl CircularQueue {
fn enqueue(&mut self, value: i32) {
if self.size == self.capacity { return; }
self.data[self.rear] = value;
self.rear = (self.rear + 1) % self.capacity;
self.size += 1;
}
fn dequeue(&mut self) -> Option<i32> {
if self.size == 0 { return None; }
let val = self.data[self.front];
self.front = (self.front + 1) % self.capacity;
self.size -= 1;
Some(val)
}
}
This code adds (enqueue) and removes (dequeue) elements in a circular queue using an array.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Single-step index updates and assignments in enqueue and dequeue.
- How many times: Each enqueue or dequeue runs once per call, no loops inside.
Each enqueue or dequeue operation does a fixed number of steps regardless of queue size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 5 steps per operation |
| 100 | 5 steps per operation |
| 1000 | 5 steps per operation |
Pattern observation: The time per operation stays the same no matter how big the queue is.
Time Complexity: O(1)
This means adding or removing an item takes the same short time no matter how many items are in the queue.
[X] Wrong: "Because the queue uses an array, enqueue or dequeue must take longer as the queue grows."
[OK] Correct: The circular queue uses fixed index moves without shifting elements, so each operation stays quick regardless of size.
Understanding constant-time queue operations shows you know how to manage data efficiently, a key skill in many coding challenges.
"What if we replaced the array with a linked list? How would the time complexity of enqueue and dequeue change?"
Practice
Solution
Step 1: Understand linear queue limitation
In a linear queue using an array, once the rear reaches the end, no more elements can be added even if there is free space at the front.Step 2: Circular queue advantage
A circular queue connects the end of the array back to the front, allowing reuse of empty spaces and efficient use of the array.Final Answer:
It efficiently uses all available space by wrapping around the array. -> Option AQuick Check:
Circular queue = efficient space use [OK]
- Thinking circular queue sorts elements
- Believing it uses less memory than linear queue
- Assuming it stores different data types
Solution
Step 1: Check array initialization
let mut queue: [i32; 5] = [0; 5]; let mut front: i32 = -1; let mut rear: i32 = -1; initializes a fixed-size array of 5 integers with all zeros, which is correct for a circular queue.Step 2: Check front and rear initialization
let mut queue: [i32; 5] = [0; 5]; let mut front: i32 = -1; let mut rear: i32 = -1; sets front and rear to -1, indicating the queue is empty, which is standard practice.Final Answer:
let mut queue: [i32; 5] = [0; 5]; let mut front: i32 = -1; let mut rear: i32 = -1; -> Option DQuick Check:
Correct array and pointers initialization = let mut queue: [i32; 5] = [0; 5]; let mut front: i32 = -1; let mut rear: i32 = -1; [OK]
- Using Vec instead of fixed-size array
- Not initializing front and rear to -1
- Leaving variables uninitialized
let mut queue = [0; 5];
let mut front: i32 = -1;
let mut rear: i32 = -1;
// Enqueue 10
if rear == -1 {
front = 0;
rear = 0;
queue[rear as usize] = 10;
} else {
rear = (rear + 1) % 5;
queue[rear as usize] = 10;
}
// Enqueue 20
rear = (rear + 1) % 5;
queue[rear as usize] = 20;
// Dequeue
front = (front + 1) % 5;
// Enqueue 30
rear = (rear + 1) % 5;
queue[rear as usize] = 30;
Solution
Step 1: Trace enqueue and dequeue operations
Initially front = -1, rear = -1, queue = [0,0,0,0,0]. - Enqueue 10: front=0, rear=0, queue[0]=10 -> [10,0,0,0,0] - Enqueue 20: rear=(0+1)%5=1, queue[1]=20 -> [10,20,0,0,0] - Dequeue: front=(0+1)%5=1 (10 removed logically) - Enqueue 30: rear=(1+1)%5=2, queue[2]=30 -> [10,20,30,0,0]Step 2: Determine final queue state
Queue array is [10, 20, 30, 0, 0]. Although 10 is logically dequeued, it remains in the array until overwritten.Final Answer:
[10, 20, 0, 30, 0] -> Option CQuick Check:
After dequeue, front moves, old value stays [OK]
- Assuming dequeued elements are erased immediately
- Not applying modulo for rear and front
- Mixing up front and rear positions
fn enqueue(queue: &mut [i32; 5], front: &mut i32, rear: &mut i32, value: i32) {
if (*rear + 1) % 5 == *front {
println!("Queue is full");
return;
}
if *rear == -1 {
*front = 0;
*rear = 0;
} else {
*rear = (*rear + 1) % 5;
}
queue[*rear as usize] = value;
}
Solution
Step 1: Analyze empty queue check
The code checks if *rear == -1 to detect empty queue, but standard practice is to check if *front == -1 because front indicates the start of queue.Step 2: Understand impact of wrong check
If *rear == -1 but *front is not -1, the queue might be incorrectly treated as empty, causing logic errors.Final Answer:
The initial check for empty queue should be *front == -1, not *rear == -1. -> Option BQuick Check:
Empty queue check uses front pointer [OK]
- Checking rear instead of front for empty queue
- Incorrect modulo usage
- Not passing mutable references properly
Solution
Step 1: Understand initial full queue state
Queue size = 4, front = 2, rear = 1 means the queue is full because (rear + 1) % size == front ( (1+1)%4=2 ).Step 2: Perform dequeue operation
Dequeue moves front forward: front = (2 + 1) % 4 = 3. Rear remains 1.Step 3: Perform first enqueue (50)
rear = (1 + 1) % 4 = 2, insert 50 at index 2.Step 4: Perform second enqueue (60)
rear = (2 + 1) % 4 = 3, insert 60 at index 3.Final Answer:
front = 3, rear = 3 -> Option AQuick Check:
After dequeue and two enqueues, pointers wrap correctly [OK]
- Not applying modulo after pointer increment
- Confusing front and rear updates
- Assuming queue is empty after dequeue
Ready to prove it?
Pass the test and this pattern moves unknown
