Queue Implementation Using Array in DSA Go - Time & Space Complexity
We want to understand how the time taken by queue operations changes as the number of elements grows.
How does adding or removing items from a queue using an array affect performance?
Analyze the time complexity of the following queue operations implemented with an array.
package main
import "fmt"
type Queue struct {
items []int
}
func (q *Queue) Enqueue(val int) {
q.items = append(q.items, val)
}
func (q *Queue) Dequeue() int {
val := q.items[0]
for i := 0; i < len(q.items)-1; i++ {
q.items[i] = q.items[i+1]
}
q.items = q.items[:len(q.items)-1]
return val
}
func main() {
q := Queue{}
q.Enqueue(1)
q.Enqueue(2)
fmt.Println(q.Dequeue())
}
This code adds items to the end and removes from the front of an array-based queue.
Look at what repeats when we add or remove items.
- Primary operation: Adding to the end (append) and removing from the front (slice update)
- How many times: Each enqueue or dequeue runs once per operation, but dequeue shifts the array slice which can copy elements
Adding items grows smoothly, but removing items requires shifting elements.
| Input Size (n) | Approx. Operations for Dequeue |
|---|---|
| 10 | About 9 moves |
| 100 | About 99 moves |
| 1000 | About 999 moves |
Pattern observation: Dequeue operation cost grows linearly with the number of items because elements shift left each time.
Time Complexity: O(n) for Dequeue, O(1) amortized for Enqueue
Removing an item takes longer as the queue grows, but adding is fast on average.
[X] Wrong: "Both enqueue and dequeue are always O(1) because arrays are fast."
[OK] Correct: Removing from the front requires shifting all other elements, which takes time proportional to the queue size.
Understanding these costs helps you explain why some queue implementations use linked lists or circular buffers for better performance.
"What if we used a circular array with front and rear pointers instead of slicing? How would the time complexity change?"
Practice
Solution
Step 1: Understand queue behavior
A queue adds elements at the rear and removes from the front, so the first element added is the first removed.Step 2: Match behavior with options
This behavior is called First-In-First-Out (FIFO), which matches It follows First-In-First-Out (FIFO) order.Final Answer:
It follows First-In-First-Out (FIFO) order -> Option CQuick Check:
Queue = FIFO [OK]
- Confusing queue with stack (LIFO)
- Thinking queue allows random access
- Assuming queue sorts elements automatically
Solution
Step 1: Understand array declaration in Go
Fixed-size arrays in Go are declared with var name [size]type, e.g., var queue [5]int.Step 2: Check options for fixed-size array
var queue [5]int uses var queue [5]int which is correct fixed-size array syntax.Final Answer:
var queue [5]int -> Option AQuick Check:
Fixed array = var name [size]type [OK]
- Using make for fixed-size array (make is for slices)
- Using slice literal with size inside braces
- Confusing slice and array syntax
var queue [5]int front, rear := -1, -1 // Enqueue 10 rear++ queue[rear] = 10 // Enqueue 20 rear++ queue[rear] = 20 // Dequeue front++ // Enqueue 30 rear++ queue[rear] = 30 // Current queue elements from front+1 to rear are printedWhat will be the printed queue elements?
Solution
Step 1: Trace enqueue and dequeue operations
Initially front = -1, rear = -1. Enqueue 10: rear=0, queue[0]=10 Enqueue 20: rear=1, queue[1]=20 Dequeue: front=0 (removes queue[0]=10) Enqueue 30: rear=2, queue[2]=30Step 2: Identify current queue elements
Queue elements are from front+1=1 to rear=2, so queue[1]=20 and queue[2]=30. Printed elements: [20, 30]Final Answer:
[20, 30] -> Option AQuick Check:
After dequeue, front=0; elements = queue[1..2] = [20,30] [OK]
- Printing elements from 0 to rear ignoring front
- Not updating front after dequeue
- Confusing front and rear indices
func dequeue() int {
if front == rear {
fmt.Println("Queue is empty")
return -1
}
front++
return queue[front]
}
What is the main bug in this code?Solution
Step 1: Understand dequeue logic
Dequeue removes element at front+1 index. Incrementing front before returning queue[front] is correct to get the next element.Step 2: Analyze options for bug
It does not check if queue is full before dequeue is unrelated to dequeue. It increments front before returning element, causing off-by-one error says increment causes off-by-one error but this is standard for queue. It should decrement rear instead of front is wrong because rear is for enqueue. It returns queue[front] after increment, which is correct correctly states the code is correct.Final Answer:
It returns queue[front] after increment, which is correct -> Option DQuick Check:
Increment front then return element = correct dequeue [OK]
- Thinking front should not be incremented before return
- Confusing enqueue and dequeue pointers
- Checking full condition in dequeue
Enqueue 5 Enqueue 10 Enqueue 15 Dequeue Dequeue Enqueue 20 Enqueue 25 Enqueue 30 Enqueue 35What will happen when you try to enqueue 35, and what is the correct way to handle this situation?
Solution
Step 1: Trace queue operations and indices
Initial front = -1, rear = -1 Enqueue 5: rear=0 Enqueue 10: rear=1 Enqueue 15: rear=2 Dequeue: front=0 (removes 5) Dequeue: front=1 (removes 10) Enqueue 20: rear=3 Enqueue 25: rear=4 Enqueue 30: rear=5 (array size 5, index 5 invalid - overflow) Trying to enqueue 35 causes overflowStep 2: Explain correct handling
Since front has moved forward, space at start is free but rear reached end. Using circular queue logic allows rear to wrap to start and reuse space. Without circular logic, overflow occurs prematurely.Final Answer:
Queue overflow occurs; use circular queue logic to reuse space -> Option BQuick Check:
Overflow at end; circular queue fixes space reuse [OK]
- Ignoring free space at start after dequeues
- Assuming array can grow dynamically
- Resetting front and rear incorrectly
Ready to prove it?
Pass the test and this pattern moves unknown
