Circular Queue Implementation Using Array in DSA Python - Time & Space Complexity
We want to understand how fast operations run in a circular queue using an array.
Specifically, how the time to add or remove items changes as the queue grows.
Analyze the time complexity of the following code snippet.
class CircularQueue:
def __init__(self, size):
self.size = size
self.queue = [None] * size
self.front = -1
self.rear = -1
def enqueue(self, value):
if (self.rear + 1) % self.size == self.front:
return "Queue is full"
if self.front == -1:
self.front = 0
self.rear = (self.rear + 1) % self.size
self.queue[self.rear] = value
def dequeue(self):
if self.front == -1:
return "Queue is empty"
data = self.queue[self.front]
if self.front == self.rear:
self.front = self.rear = -1
else:
self.front = (self.front + 1) % self.size
return data
This code adds (enqueue) and removes (dequeue) items in a circular queue using an array.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Single-step index update and assignment in enqueue and dequeue.
- How many times: Each enqueue or dequeue runs once per call, no loops inside.
Each enqueue or dequeue does a fixed number of steps regardless of queue size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 5-6 steps per operation |
| 100 | 5-6 steps per operation |
| 1000 | 5-6 steps per operation |
Pattern observation: The number of steps stays about 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 check many elements, so it takes longer as the queue grows."
[OK] Correct: The circular queue uses index math to jump directly to the right spot, so it does not scan or loop through elements.
Understanding constant time operations in data structures like circular queues shows you can design efficient systems that handle data smoothly.
"What if we changed the circular queue to a normal queue using a list that shifts elements on dequeue? How would the time complexity change?"
Practice
Solution
Step 1: Understand linear queue limitation
In a linear queue using an array, once rear reaches the end, no more elements can be added even if there is space at the front.Step 2: Circular queue reuses space
Circular queue connects the end of the array back to the front, allowing reuse of freed slots and efficient space usage.Final Answer:
It efficiently uses the available space by reusing freed slots. -> Option AQuick Check:
Space reuse = Advantage [OK]
- Thinking circular queue sorts elements
- Believing circular queue removes need for pointers
- Assuming it stores multiple data types automatically
max_size with front and rear pointers?Solution
Step 1: Understand full condition in circular queue
The queue is full when the next position of rear (circularly) is front.Step 2: Use modulo for circular increment
Using modulo operator, (rear + 1) % max_size gives the next position circularly.Final Answer:
if (rear + 1) % max_size == front -> Option DQuick Check:
Full condition = (rear + 1) % max_size == front [OK]
- Checking rear == front means full (actually empty)
- Using rear == max_size - 1 ignores circular wrap
- Checking front == -1 means empty, not full
front = 0 rear = 3 queue = [10, 20, 30, 40, None] # Enqueue operation rear = (rear + 1) % 5 queue[rear] = 50 # Dequeue operation front = (front + 1) % 5 print(queue)
What will be the printed output?
Solution
Step 1: Perform enqueue operation
rear moves from 3 to (3+1)%5 = 4; queue[4] = 50; queue becomes [10, 20, 30, 40, 50]Step 2: Perform dequeue operation
front moves from 0 to (0+1)%5 = 1; front=1, rear=4Final Answer:
[10, 20, 30, 40, 50] with front=1 and rear=4 -> Option AQuick Check:
Enqueue rear=4, Dequeue front=1 [OK]
- Ignoring front and rear pointer updates
- Assuming queue content changes on dequeue
- Confusing front and rear positions
def dequeue(queue, front, rear, max_size):
if front == -1:
return None, front, rear # Queue empty
data = queue[front]
if front == rear:
front = rear = -1
else:
front = (front + 1) % max_size
return data, front, rear
# Initial state
queue = [10, 20, 30, 40, 50]
front = 0
rear = 4
# Dequeue call
value, front, rear = dequeue(queue, front, rear, 5)
print(value, front, rear)What is the bug in this code?
Solution
Step 1: Check queue update after dequeue
The dequeue operation removes the element logically by moving front, but the queue array still holds the old value.Step 2: Identify missing update
To avoid stale data, the dequeued position should be cleared or updated (e.g., set to None) after dequeue.Final Answer:
The function does not update the queue after dequeue. -> Option BQuick Check:
Queue array update after dequeue [OK]
- Checking only front for empty queue
- Not resetting pointers correctly after last dequeue
- Ignoring queue full condition in dequeue
front = 4 and rear = 2. The queue array is:[70, 80, 90, None, 40, 50]Which of the following sequences of operations will correctly enqueue 100 and dequeue two elements, updating front and rear properly?
Solution
Step 1: Enqueue operation
rear = (2 + 1) % 6 = 3; queue[3] = 100; queue becomes [70, 80, 90, 100, 40, 50]Step 2: Dequeue two elements
front moves twice: front = (4 + 1) % 6 = 5, then front = (5 + 1) % 6 = 0Final Answer:
Enqueue: rear = (rear + 1) % 6; queue[rear] = 100; Dequeue: front = (front + 1) % 6; front = (front + 1) % 6 -> Option CQuick Check:
One enqueue + two dequeues update pointers correctly [OK]
- Incrementing rear or front by more than 1 at once
- Not using modulo for circular wrap
- Dequeueing fewer or more times than asked
Ready to prove it?
Pass the test and this pattern moves unknown
