Queue Implementation Using Linked List in DSA Javascript - Time & Space Complexity
We want to understand how the time to add or remove items from a queue changes as the queue grows.
How does the queue's size affect the speed of enqueue and dequeue operations?
Analyze the time complexity of the following queue operations using a linked list.
class Node {
constructor(value) {
this.value = value;
this.next = null;
}
}
class Queue {
constructor() {
this.front = null;
this.rear = null;
}
enqueue(value) {
const newNode = new Node(value);
if (!this.rear) {
this.front = this.rear = newNode;
return;
}
this.rear.next = newNode;
this.rear = newNode;
}
dequeue() {
if (!this.front) return null;
const temp = this.front;
this.front = this.front.next;
if (!this.front) this.rear = null;
return temp.value;
}
}
This code adds items to the end and removes items from the front of the queue using a linked list.
Look for loops or repeated steps inside enqueue and dequeue.
- Primary operation: Adding or removing one node by changing pointers.
- How many times: Each operation does a fixed number of steps, no loops.
Adding or removing one item always takes the same number of steps, no matter how big the queue is.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 5-6 steps per enqueue or dequeue |
| 100 | 5-6 steps per enqueue or dequeue |
| 1000 | 5-6 steps per enqueue or dequeue |
Pattern observation: The number of steps stays the same regardless of queue size.
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: "Removing an item from the queue takes longer as the queue grows because it has to move through all items."
[OK] Correct: The linked list keeps track of the front, so removing just changes a pointer without walking through the list.
Knowing that queue operations are fast and constant time helps you explain efficient data handling in real projects.
"What if we used an array instead of a linked list for the queue? How would the time complexity of dequeue change?"
Practice
Queue using a linked list instead of an array?Solution
Step 1: Understand queue size limitations in arrays
Arrays have fixed size or require resizing, which can be costly.Step 2: Linked list dynamic size advantage
Linked lists grow and shrink dynamically without fixed capacity limits.Final Answer:
Dynamic size without fixed capacity limits -> Option DQuick Check:
Linked list queues grow dynamically [OK]
- Thinking linked lists allow faster random access
- Assuming linked lists always use less memory
- Confusing syntax simplicity with performance
Solution
Step 1: Identify tail pointer usage in queue enqueue
New nodes are added at the tail; tail.next points to new node.Step 2: Update tail to new node
After adding, tail must point to the new node to keep queue end updated.Final Answer:
tail.next = newNode; tail = newNode; -> Option AQuick Check:
Enqueue adds at tail [OK]
- Adding new node at head instead of tail
- Not updating tail pointer after insertion
- Confusing head and tail pointers
peek() for a linked list queue that returns the front element without removing it. Which is the correct implementation?Solution
Step 1: Understand peek operation in queue
Peek returns front element without removing it, which is at head in linked list queue.Step 2: Check correct pointer for front element
Head points to front node; tail points to end node. So return head.value if head exists.Final Answer:
return this.head ? this.head.value : null; -> Option AQuick Check:
Peek returns head value [OK]
- Returning tail value instead of head
- Accessing next of tail which is always null
- Returning head.next value which skips front
class Node {
constructor(value) {
this.value = value;
this.next = null;
}
}
class Queue {
constructor() {
this.head = null;
this.tail = null;
}
enqueue(value) {
const newNode = new Node(value);
if (!this.tail) {
this.head = newNode;
this.tail = newNode;
return;
}
this.tail.next = newNode;
this.tail = newNode;
}
dequeue() {
if (!this.head) return null;
const val = this.head.value;
this.head = this.head.next;
if (!this.head) this.tail = null;
return val;
}
}
const q = new Queue();
q.enqueue(10);
q.enqueue(20);
q.dequeue();
q.enqueue(30);
q.dequeue();
q.enqueue(40);
q.enqueue(50);
q.dequeue();
console.log(q.head.value);
What will be printed?Solution
Step 1: Trace enqueue and dequeue operations
Queue after operations: - enqueue 10: head=10, tail=10 - enqueue 20: head=10->20, tail=20 - dequeue: removes 10, head=20 - enqueue 30: head=20->30, tail=30 - dequeue: removes 20, head=30 - enqueue 40: head=30->40, tail=40 - enqueue 50: head=30->40->50, tail=50 - dequeue: removes 30, head=40Step 2: Identify current head value
After last dequeue, head points to node with value 40.Final Answer:
40 -> Option BQuick Check:
Final head value after operations = 40 [OK]
- Forgetting to update tail when queue becomes empty
- Mixing up enqueue and dequeue order
- Assuming head value is unchanged after dequeue
dequeue() {
if (!this.head) return null;
const val = this.head.value;
this.head = this.head.next;
return val;
}
What is the problem?Solution
Step 1: Analyze dequeue method behavior
It removes head by moving head pointer to next node and returns old head value.Step 2: Identify missing tail update
If after removal head becomes null (queue empty), tail still points to old node causing inconsistency.Final Answer:
It does not update tail when queue becomes empty -> Option CQuick Check:
Empty queue must reset tail = null [OK]
- Ignoring tail pointer update on empty queue
- Confusing head and tail roles
- Assuming dequeue removes tail node
Ready to prove it?
Pass the test and this pattern moves unknown
