Skip to content

3.11 Insertion Patterns

Insertion adds nodes into a linked list by creating new links while preserving reachability of all existing nodes.

Insertion adds nodes into a linked list by creating new links while preserving reachability of all existing nodes. In a singly linked list, insertion is defined relative to a known position, usually a predecessor node. In a doubly linked list, insertion can be defined relative to either neighbor.

Problem

Given a linked list and a rule for placement, insert a new node or a sequence of nodes while maintaining structural correctness.

Typical rules include:

insert at head
insert at tail
insert after a node
insert before a node
insert in sorted order
insert at position k

Core Pattern

Insertion requires two assignments:

new.next = next_node
prev.next = new

The order matters. First connect the new node to the remainder of the list, then connect the predecessor to the new node.

Insert at Head

new.next = head
head = new

This runs in O(1). If a sentinel is used, this becomes:

new.next = dummy.next
dummy.next = new

Insert at Tail

If the tail pointer is available:

tail.next = new
new.next = null
tail = new

Without a tail pointer, traversal is required.

cur = head
while cur.next != null:
  cur = cur.next

cur.next = new
new.next = null

This runs in O(n).

Insert After a Given Node

new.next = cur.next
cur.next = new

This is O(1). The invariant is that cur remains reachable and now points to new.

Insert Before a Given Node

In a singly linked list, insertion before a node requires access to its predecessor. Use a sentinel or maintain prev.

dummy.next = head

prev = dummy
cur = head

while cur != null:
  if cur == target:
    new.next = cur
    prev.next = new
    break
  prev = cur
  cur = cur.next

head = dummy.next

In a doubly linked list, insertion before is direct.

before = cur.prev

new.prev = before
new.next = cur

before.next = new
cur.prev = new

Insert at Position k

Insert at index k (0-based). Use a sentinel for uniform handling.

dummy.next = head
prev = dummy

for i in 0..k-1:
  if prev.next == null:
    break
  prev = prev.next

new.next = prev.next
prev.next = new

return dummy.next

If k = 0, insertion occurs at the head.

Insert into a Sorted List

Find the first node with value greater than or equal to the new value.

dummy.next = head

prev = dummy
cur = head

while cur != null and cur.value < new.value:
  prev = cur
  cur = cur.next

new.next = cur
prev.next = new

return dummy.next

This preserves sorted order and stability.

Batch Insertion

Insert a sequence of nodes after a position.

after = prev.next

prev.next = first_new
last_new.next = after

This pattern connects an entire segment in O(1), assuming the segment endpoints are known.

Insert While Traversing

Sometimes insertion happens during traversal.

cur = head

while cur != null:
  if condition(cur):
    new = make_node(...)
    new.next = cur.next
    cur.next = new
    cur = new.next
  else:
    cur = cur.next

The step cur = new.next avoids reprocessing the inserted node.

Doubly Linked List Insertion

General insertion between two nodes:

insert_between(left, right, new):
  new.prev = left
  new.next = right
  left.next = new
  right.prev = new

This uniform pattern works for all positions when sentinel nodes are present.

Complexity Summary

PatternTimeSpace
Insert at headO(1)O(1)
Insert at tail (with tail pointer)O(1)O(1)
Insert at tail (no tail pointer)O(n)O(1)
Insert after nodeO(1)O(1)
Insert before node (singly)O(n)O(1)
Insert at position kO(k)O(1)
Insert in sorted listO(n)O(1)

Common Failure Modes

The most common mistake is reversing the order of assignments. Writing:

prev.next = new
new.next = cur

is correct only if cur is still reachable. If prev.next is overwritten before saving the old successor, the rest of the list may be lost.

Another failure is not updating head when inserting at position zero. A sentinel avoids this issue.

In traversal-based insertion, failing to advance correctly after insertion can lead to infinite loops or repeated insertions.

Key Insight

Insertion is local but must preserve global reachability. The safe pattern is: identify the predecessor, save the successor, connect the new node to the successor, then connect the predecessor to the new node. This sequence guarantees that no part of the list is lost.