Dequeue Using Linked List in DSA Javascript - Time & Space Complexity
We want to understand how fast operations run when using a dequeue built with a linked list.
How does the time to add or remove items change as the list grows?
Analyze the time complexity of the following code snippet.
class Node {
constructor(value) {
this.value = value;
this.next = null;
this.prev = null;
}
}
class Dequeue {
constructor() {
this.head = null;
this.tail = null;
}
addFront(value) {
const node = new Node(value);
if (!this.head) {
this.head = this.tail = node;
} else {
node.next = this.head;
this.head.prev = node;
this.head = node;
}
}
removeRear() {
if (!this.tail) return null;
const value = this.tail.value;
this.tail = this.tail.prev;
if (this.tail) this.tail.next = null;
else this.head = null;
return value;
}
}
This code shows adding an item to the front and removing an item from the rear of a dequeue using a linked list.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Adding or removing a node involves changing a few pointers.
- How many times: Each add or remove runs once per call, no loops inside.
Each add or remove operation takes the same small number of steps no matter how many items are in the dequeue.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 5-7 steps |
| 100 | 5-7 steps |
| 1000 | 5-7 steps |
Pattern observation: The time stays about the same no matter how big the dequeue gets.
Time Complexity: O(1)
This means adding or removing from either end takes a constant amount of time, no matter the size.
[X] Wrong: "Removing from the rear takes longer because you have to find the last node."
[OK] Correct: The linked list keeps a direct pointer to the tail, so removal is immediate without searching.
Knowing that dequeue operations run in constant time shows you understand efficient data structure design, a key skill in interviews.
"What if we used a singly linked list without a tail pointer? How would the time complexity of removing from the rear change?"
Practice
Solution
Step 1: Understand linked list operations
A linked list allows adding or removing nodes at the front or back in constant time without moving other elements.Step 2: Compare with array behavior
Arrays require shifting elements when inserting or deleting at the front, which is less efficient.Final Answer:
It allows efficient insertion and deletion from both ends without shifting elements. -> Option BQuick Check:
Linked list = efficient ends operations [OK]
- Thinking arrays are always more memory efficient
- Assuming linked list sorts elements automatically
- Believing linked list prevents duplicates
Solution
Step 1: Understand adding at front in linked list
To add at front, new node's next points to current head, then head updates to new node.Step 2: Check options for correctness
Create new node, set its next to current head, update head to new node. matches this logic; others incorrectly use prev or tail.Final Answer:
Create new node, set its next to current head, update head to new node. -> Option DQuick Check:
Add front = newNode.next = head; head = newNode [OK]
- Confusing prev and next pointers
- Updating tail instead of head when adding front
- Setting next to null incorrectly
class Node {
constructor(value) {
this.value = value;
this.next = null;
this.prev = null;
}
}
class Dequeue {
constructor() {
this.head = null;
this.tail = null;
}
addFront(value) {
const newNode = new Node(value);
if (!this.head) {
this.head = this.tail = newNode;
} else {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
}
}
addRear(value) {
const newNode = new Node(value);
if (!this.tail) {
this.head = this.tail = newNode;
} else {
this.tail.next = newNode;
newNode.prev = this.tail;
this.tail = newNode;
}
}
removeFront() {
if (!this.head) return null;
const val = this.head.value;
this.head = this.head.next;
if (this.head) this.head.prev = null;
else this.tail = null;
return val;
}
}
const dq = new Dequeue();
dq.addRear(10);
dq.addFront(20);
dq.addRear(30);
const removed = dq.removeFront();
console.log(removed);
console.log(dq.head.value + ' -> ' + dq.tail.value);
What will be the output?Solution
Step 1: Trace insertions
addRear(10): list is 10 addFront(20): list is 20 -> 10 addRear(30): list is 20 -> 10 -> 30Step 2: Trace removal
removeFront() removes 20, returns 20. List becomes 10 -> 30.Final Answer:
20 10 -> 30 -> Option CQuick Check:
Remove front returns 20; list head=10, tail=30 [OK]
- Confusing addFront and addRear order
- Forgetting to update prev pointers
- Mixing removed value with current head
removeRear() {
if (!this.tail) return null;
const val = this.tail.value;
this.tail = this.tail.prev;
this.tail.next = null;
return val;
}Solution
Step 1: Analyze removal logic
After setting tail to tail.prev, if tail becomes null (list empty), accessing tail.next causes error.Step 2: Identify missing check
Code must check if tail is null before setting tail.next = null to avoid runtime error.Final Answer:
It does not handle the case when the list becomes empty after removal. -> Option AQuick Check:
Check empty list after removal to avoid null pointer [OK]
- Not checking if tail is null after update
- Confusing head and tail pointers
- Setting wrong pointer to null
reverseDequeue() that reverses the order of nodes in a dequeue implemented using a doubly linked list. Which approach correctly reverses the dequeue?Solution
Step 1: Understand reversal in doubly linked list
Reversing means each node's next and prev pointers must be swapped to reverse direction.Step 2: Update head and tail pointers
After swapping pointers in all nodes, head and tail pointers must be swapped to reflect new ends.Final Answer:
Traverse each node, swap its next and prev pointers, then swap head and tail pointers. -> Option AQuick Check:
Swap next/prev in nodes + swap head/tail [OK]
- Only swapping head and tail values
- Setting next to null incorrectly
- Creating new dequeue instead of reversing in place
Ready to prove it?
Pass the test and this pattern moves unknown
