Insert at End of Doubly Linked List in DSA Typescript - 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 {
value: number;
next: Node | null = null;
prev: Node | null = null;
constructor(value: number) {
this.value = value;
}
}
function insertAtEnd(head: Node | null, value: number): Node {
const newNode = new Node(value);
if (!head) return newNode;
let current = head;
while (current.next) {
current = current.next;
}
current.next = newNode;
newNode.prev = current;
return head;
}
This code adds a new node with the given value at the end of a doubly linked list by walking through the list from the start.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The while loop that moves through each node until the last one.
- How many times: It runs once for each node in the list until the end is reached.
As the list gets longer, the time to find the end grows roughly in direct proportion to the number of nodes.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 steps to reach the end |
| 100 | About 100 steps to reach the end |
| 1000 | About 1000 steps to reach the end |
Pattern observation: The steps increase linearly as the list size grows.
Time Complexity: O(n)
This means the time to insert at the end grows directly with the number of nodes in the list.
[X] Wrong: "Inserting at the end is always fast and takes constant time."
[OK] Correct: Without a pointer to the last node, we must walk through the whole list, so time grows with list size.
Understanding this helps you explain how linked lists work and why keeping track of the tail node can speed up insertions.
"What if we kept a direct reference to the last node? How would the time complexity change?"
Practice
Solution
Step 1: Understand the structure of a doubly linked list
A doubly linked list has nodes connected forward and backward, 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, updating pointers accordingly.Final Answer:
To add a new element after the last node -> Option AQuick Check:
Insert at end = add after last node [OK]
- Confusing insertion at end with deletion
- Thinking insertion reverses the list
- Assuming insertion happens at the start
Solution
Step 1: Identify required properties for a doubly linked list node
A node must have data, next pointer, and prev pointer to link both directions.Step 2: Check each option for correct properties
const newNode = { data: 5, next: null, prev: null }; has data, next, and prev all properly set. Others miss prev or use wrong property names.Final Answer:
const newNode = { data: 5, next: null, prev: null }; -> Option BQuick Check:
Node needs data, next, prev [OK]
- Omitting prev pointer
- Using wrong property names like value or nextNode
- Setting next or prev as undefined instead of null
class Node {
data: number;
next: Node | null = null;
prev: Node | null = null;
constructor(data: number) { this.data = data; }
}
class DoublyLinkedList {
head: Node | null = null;
insertAtEnd(data: number) {
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: Trace insertAtEnd calls
First insert adds 10 as head. Second adds 20 after 10. Third adds 30 after 20.Step 2: Trace printList output
Print starts at head (10), then 20, then 30, then null.Final Answer:
10 -> 20 -> 30 -> null -> Option AQuick Check:
Inserted nodes in order = 10 -> 20 -> 30 [OK]
- Confusing order and printing reversed list
- Missing last inserted node
- Printing null instead of list
insertAtEnd(data: number) {
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 empty list case
If head is null, method sets head to newNode but continues to loop and assign next, causing error.Step 2: Fix by adding return after setting head
Adding return after setting head prevents further code execution and avoids null pointer errors.Final Answer:
Missing return after setting head when list is empty -> Option DQuick Check:
Return after head set avoids errors [OK]
- Not returning after setting head
- Misunderstanding loop condition
- Incorrect pointer assignments
Solution
Step 1: Understand insertion at end
The new node 40 should be linked after 30, so 30.next = 40 and 40.prev = 30.Step 2: Confirm pointers of new node
Since 40 is last, 40.next must be null to mark list end.Final Answer:
10 <-> 20 <-> 30 <-> 40, where 40.prev points to 30 and 40.next is null -> Option CQuick Check:
New tail prev = old tail, next = null [OK]
- Setting new node's next pointer incorrectly
- Not linking new node's prev pointer
- Inserting new node at start instead of end
Ready to prove it?
Pass the test and this pattern moves unknown
