Insertion in a doubly linked list means adding a new node at a specific position—either at the beginning, in the middle, or at the end of the list.
Unlike singly linked lists, each node in a doubly linked list has two pointers: next and prev. When inserting a node, both pointers must be carefully updated to preserve the two-way connectivity.
Suppose we have a doubly linked list:
We want to insert 30 at index 2, so the final list becomes: 10 ⇄ 20 ⇄ 30 ⇄ 40 ⇄ null
10 ⇄ 20 ⇄ 40 ⇄ null.1 (value 20).30.newNode.next = current.next (i.e., 30 points forward to 40).newNode.prev = current (i.e., 30 points back to 20).current.next.prev = newNode (i.e., 40’s previous becomes 30).current.next = newNode (i.e., 20’s next becomes 30).This pseudocode shows how to insert a node at a specific index in a doubly linked list.
function insertAt(head, index, value):
newNode = new DoublyListNode(value)
if index == 0:
newNode.next = head
if head != null:
head.prev = newNode
return newNode
curr = head
for i in range(index - 1):
curr = curr.next
newNode.next = curr.next
newNode.prev = curr
if curr.next != null:
curr.next.prev = newNode
curr.next = newNode
return head
Let’s walk through each part of the code to understand what it does.
function insertAt(head, index, value):
newNode = new DoublyListNode(value)
Explanation: We define a function to insert a new node at a given index in a doubly linked list. A new node is created with the specified value.
if index == 0:
newNode.next = head
if head != null:
head.prev = newNode
return newNode
Explanation: If the index is 0, the new node becomes the new head. We update the next and prev links appropriately and return the new node as the head.
curr = head
for i in range(index - 1):
curr = curr.next
Explanation: If we're inserting elsewhere, we move curr to the node just before the desired index using a loop.
newNode.next = curr.next
newNode.prev = curr
if curr.next != null:
curr.next.prev = newNode
curr.next = newNode
Explanation: After locating the correct position, we update the forward and backward links. We ensure that the previous and next pointers on both sides of the new node are properly adjusted.
return head
Explanation: Finally, we return the unchanged head of the list so the outer structure remains intact and usable.
The following examples demonstrate how to insert a new node at various positions in a doubly linked list. Each node maintains both prev and next links.
| Initial List | Insert Index | Insert Value | Final Linked List | Explanation |
|---|---|---|---|---|
| [10, 20, 30] | 0 | 5 | null ← 5 ⇄ 10 ⇄ 20 ⇄ 30 → null | Insert at the beginning. New node becomes the new head. Its next points to the old head, and the old head's prev is updated. |
| [10, 20, 30] | 2 | 25 | 10 ⇄ 20 ⇄ 25 ⇄ 30 | Insert in the middle. New node's prev and next pointers link to neighbors; neighbors update their links accordingly. |
| [10, 20, 30] | 3 | 35 | 10 ⇄ 20 ⇄ 30 ⇄ 35 → null | Insert at the end. New node's prev points to the old tail, and the old tail’s next points to the new node. |
| [10, 20, 30] | 5 | 50 | Error | Invalid index. The index is beyond the list size. The operation should fail gracefully or throw an error. |
| Insertion Case | Time Complexity | Explanation |
|---|---|---|
| Insert at Beginning | O(1) | We update the new node’s next to point to the current head and its prev to null. Then update the head’s prev and move head to the new node. |
| Insert at Middle | O(n) | We traverse the list to the desired position. Once there, we adjust both prev and next pointers of surrounding nodes and the new node. |
| Insert at End (without tail pointer) | O(n) | We traverse the list to the last node and attach the new node by updating next and prev pointers. |
| Insert at End (with tail pointer) | O(1) | With a tail pointer, we can insert the new node directly after the last node and update the tail reference. |
| Insertion Case | Space Complexity | Explanation |
|---|---|---|
| Insert at Beginning | O(1) | Only one new node is created. Although the node has two pointers (prev and next), they are part of the node structure and do not add extra asymptotic space. |
| Insert at Middle | O(1) | Space remains constant since only one node is added, regardless of position. The new node’s prev and next pointers are set appropriately. |
| Insert at End | O(1) | Inserting at the end involves creating a single node with two pointers, which still requires constant space. |
prev and next pointers.prev and next of the new node, and possibly the neighboring nodes.next to point to the current head, and set the head’s prev to the new node.prev and next pointers of the surrounding nodes.