Queue Implementation Using Linked List in DSA Typescript - Time & Space Complexity
We want to understand how fast queue operations run when using a linked list.
Specifically, how the time to add or remove items changes as the queue grows.
Analyze the time complexity of the following queue operations using a linked list.
class Node {
value: number;
next: Node | null = null;
constructor(value: number) { this.value = value; }
}
class Queue {
front: Node | null = null;
rear: Node | null = null;
enqueue(value: number) {
const newNode = new Node(value);
if (!this.rear) {
this.front = this.rear = newNode;
} else {
this.rear.next = newNode;
this.rear = newNode;
}
}
dequeue(): number | null {
if (!this.front) return null;
const value = this.front.value;
this.front = this.front.next;
if (!this.front) this.rear = null;
return value;
}
}
This code adds items to the end and removes from the front of a linked list to form a queue.
Look for loops or repeated steps inside enqueue and dequeue.
- Primary operation: Adding or removing a single node pointer.
- How many times: Each operation does a fixed number of steps, no loops.
Each enqueue or dequeue does the same small number of steps no matter how big the queue is.
| 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 time stays about the same no matter how many items are in the queue.
Time Complexity: O(1)
This means adding or removing an item takes the same short time regardless of queue size.
[X] Wrong: "Removing from the front takes longer as the queue grows because we have to move through nodes."
[OK] Correct: We keep a direct pointer to the front node, so removal is always quick without searching.
Knowing that queue operations run in constant time shows you understand efficient data structure design.
What if we did not keep a rear pointer and had to find the end each time? How would the time complexity change?
Practice
Queue using a linked list over an array?Solution
Step 1: Understand array limitations
Arrays have fixed size or require resizing which is costly.Step 2: Linked list flexibility
Linked lists grow dynamically without fixed capacity limits.Final Answer:
Dynamic size without fixed capacity -> Option AQuick Check:
Linked list queues grow dynamically = A [OK]
- Thinking linked lists allow faster random access
- Assuming linked lists use less memory always
- Believing queues sort elements automatically
Solution
Step 1: Check property types
Node must have a value and next pointer which can be null.Step 2: Validate constructor initialization
Constructor sets value and initializes next to null.Final Answer:
class Node { value: number; next: Node | null; constructor(value: number) { this.value = value; this.next = null; } } -> Option BQuick Check:
Node next can be null, initialized in constructor = D [OK]
- Setting next to undefined instead of null
- Using wrong property names like val instead of value
- Declaring next as only Node without null option
class Node {
value: number;
next: Node | null;
constructor(value: number) {
this.value = value;
this.next = null;
}
}
class Queue {
front: Node | null = null;
rear: Node | null = null;
enqueue(value: number) {
const newNode = new Node(value);
if (!this.rear) {
this.front = this.rear = newNode;
return;
}
this.rear.next = newNode;
this.rear = newNode;
}
dequeue(): number | null {
if (!this.front) return null;
const value = this.front.value;
this.front = this.front.next;
if (!this.front) this.rear = null;
return value;
}
}
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();
What is the value returned by the last dequeue() call?Solution
Step 1: Trace enqueue and dequeue operations
Queue after operations: - enqueue 10: front=10, rear=10 - enqueue 20: front=10, rear=20 - dequeue(): removes 10, front=20 - enqueue 30: rear=30 - dequeue(): removes 20, front=30 - enqueue 40: rear=40 - enqueue 50: rear=50 - dequeue(): removes 30, front=40Step 2: Identify last dequeue return value
The last dequeue removes 30 and returns it.Final Answer:
30 -> Option CQuick Check:
Last dequeue returns 30 = A [OK]
- Confusing rear value with dequeued value
- Not updating front after dequeue
- Assuming dequeue returns rear value
dequeue() for a linked list queue:
dequeue(): number | null {
if (!this.front) return null;
const value = this.front.value;
this.front = this.front.next;
return value;
}
What is the bug in this code?Solution
Step 1: Analyze dequeue logic
It removes front node and updates front pointer correctly.Step 2: Check rear pointer update
If queue becomes empty (front is null), rear must also be set to null but code misses this.Final Answer:
It does not update rear when queue becomes empty -> Option DQuick Check:
Rear must be null if front is null after dequeue = B [OK]
- Ignoring rear pointer update on empty queue
- Assuming front check is enough
- Returning wrong value from dequeue
peek() method for your linked list queue that returns the front element without removing it. Which of the following implementations is correct?Solution
Step 1: Understand peek purpose
Peek returns front element value without removing it.Step 2: Check each option
A throws error and returns front.next value, wrong.
B returns rear value, incorrect.
C returns front value or null if empty, correct.
D returns front.next value or null, wrong.Final Answer:
peek(): number | null { return this.front ? this.front.value : null; } -> Option AQuick Check:
Peek returns front value safely = C [OK]
- Returning rear value instead of front
- Accessing front.next without checking front
- Throwing error instead of returning null on empty
Ready to prove it?
Pass the test and this pattern moves unknown
