Insert at End of Doubly Linked List in DSA Javascript - Time & Space Complexity
We want to understand how the time to insert a new item at the end of a doubly linked list changes as the list grows.
How does the number of steps needed grow when the list gets longer?
Analyze the time complexity of the following code snippet.
class Node {
constructor(data) {
this.data = data;
this.next = null;
this.prev = null;
}
}
class DoublyLinkedList {
constructor() {
this.head = null;
this.tail = null;
}
insertAtEnd(data) {
const newNode = new Node(data);
if (!this.tail) {
this.head = newNode;
this.tail = newNode;
return;
}
this.tail.next = newNode;
newNode.prev = this.tail;
this.tail = newNode;
}
}
This code adds a new node at the end of a doubly linked list by updating the tail pointer and linking nodes.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Creating a new node and updating pointers.
- How many times: Exactly once per insertion, no loops or traversals.
Each insertion does a fixed number of steps regardless of list size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 5 steps |
| 100 | 5 steps |
| 1000 | 5 steps |
Pattern observation: The number of steps stays the same no matter how long the list is.
Time Complexity: O(1)
This means inserting at the end takes the same small amount of time no matter how big the list is.
[X] Wrong: "Inserting at the end takes longer as the list grows because you have to walk through the list to find the end."
[OK] Correct: Because the list keeps a direct pointer to the tail, it does not need to walk through the list. It updates pointers directly in constant time.
Understanding this helps you explain why doubly linked lists are useful for quick insertions at both ends, a common question in coding interviews.
"What if we did not keep a tail pointer and had to find the end each time? How would the time complexity change?"
Practice
Solution
Step 1: Understand the structure of a doubly linked list
A doubly linked list has nodes with pointers to both the next and previous nodes, allowing traversal in both directions.Step 2: Purpose of insertion at the end
Inserting at the end means adding a new node after the current last node and updating the pointers accordingly.Final Answer:
To add a new element after the last node, updating both next and previous links -> Option CQuick Check:
Insert at end = Add after last node [OK]
- Confusing insertion at end with deletion
- Not updating previous pointer of new node
- Trying to insert at beginning instead
Solution
Step 1: Identify the node structure for doubly linked list
A node should have data, next pointer, and previous pointer properties.Step 2: Check each option for correct properties
const newNode = { data: value, next: null, prev: null }; correctly uses data, next, and prev keys with null pointers initially.Final Answer:
const newNode = { data: value, next: null, prev: null }; -> Option DQuick Check:
Node = data + next + prev [OK]
- Using wrong property names like value instead of data
- Missing prev pointer in node
- Using array instead of object for node
class Node {
constructor(data) {
this.data = data;
this.next = null;
this.prev = null;
}
}
class DoublyLinkedList {
constructor() {
this.head = null;
}
insertAtEnd(data) {
const newNode = new Node(data);
if (!this.head) {
this.head = newNode;
return;
}
let temp = this.head;
while (temp.next) {
temp = temp.next;
}
temp.next = newNode;
newNode.prev = temp;
}
printList() {
let temp = this.head;
let result = '';
while (temp) {
result += temp.data + ' -> ';
temp = temp.next;
}
result += 'null';
console.log(result);
}
}
const dll = new DoublyLinkedList();
dll.insertAtEnd(10);
dll.insertAtEnd(20);
dll.insertAtEnd(30);
dll.printList();Solution
Step 1: Insert nodes one by one at the end
First insert 10 as head, then 20 after 10, then 30 after 20.Step 2: Print the list from head to end
Traverse from head: 10 -> 20 -> 30 -> null is printed.Final Answer:
10 -> 20 -> 30 -> null -> Option BQuick Check:
Insert order = print order [OK]
- Confusing insertion order with reversed output
- Not updating prev pointer causing broken links
- Printing from tail instead of head
insertAtEnd(data) {
const newNode = new Node(data);
if (!this.head) {
this.head = newNode;
}
let temp = this.head;
while (temp.next) {
temp = temp.next;
}
temp.next = newNode;
newNode.prev = temp;
}Solution
Step 1: Analyze the empty list case
If head is null, newNode is assigned as head but code continues to traverse and assign next pointers.Step 2: Identify missing return causing duplicate insertion
Without return after setting head, code tries to add newNode again after head, causing a cycle or error.Final Answer:
Missing return after setting head when list is empty -> Option AQuick Check:
Return after empty insert prevents duplicate links [OK]
- Forgetting to stop function after empty list insert
- Mixing up prev and next pointers
- Assuming loop type causes error
Solution
Step 1: Insert new node at the end
New node with value 40 is added after node 30, so 30's next points to new node.Step 2: Set new node's pointers correctly
New node's prev points to 30, and next is null because it's the last node.Final Answer:
10 -> 20 -> 30 -> 40 -> null, newNode.prev points to 30, newNode.next is null -> Option AQuick Check:
Last node's next = null, newNode.prev = previous last node [OK]
- Setting newNode.prev to null incorrectly
- Placing new node at the beginning instead
- Not updating previous last node's next pointer
Ready to prove it?
Pass the test and this pattern moves unknown
