Topics › Linked List › Learn Doubly LinkedList
Insert node before head in Doubly Linked List
Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given the head of a doubly linked list and an integer val. Create a new node holding val, place it in front of the current first node, and return the head of the resulting list. Both links must be correct afterwards: the new node’s next is the old head, and the old head’s prev is the new node. The list may be empty, in which case the new node is the whole list.
Constraints: 0 ≤ n ≤ 10^5 nodes, values fit in a 32-bit signed integer. The operation must not visit the rest of the list.
Example 1
list = 2 <-> 3 <-> 4, val = 1
answer = 1 <-> 2 <-> 3 <-> 4 // 1.next is 2, 2.prev is 1, 1.prev is null
Example 2
list = (empty), val = 5
answer = 5 // prev and next are both null
Example 3
list = 9, val = 9
answer = 9 <-> 9 // two distinct nodes with equal values
The node type carries a value and two references.
static class Node {
int val;
Node prev, next;
Node(int val) { this.val = val; }
Node(int val, Node prev, Node next) { this.val = val; this.prev = prev; this.next = next; }
}
Approach 1: Link forward, link back, return
Intuition. In a singly linked list, inserting at the head is one assignment: the new node points at the old head. A doubly linked list has a mirror invariant, a.next == b if and only if b.prev == a, so every forward link we create must be matched by a backward link. Here that means one extra assignment: the old head must point back at the new node. The new node’s own prev is null because it is now first, which the constructor already gives us.
Algorithm.
- Create
node = new Node(val); itsprevandnextarenull. - Set
node.next = head. - If
headis notnull, sethead.prev = node. On an empty list there is no old head to update. - Return
nodeas the new head.
static Node insertBeforeHead(Node head, int val) {
Node node = new Node(val);
node.next = head;
if (head != null) head.prev = node; // the old head now has a predecessor; skip when the list was empty
return node;
}
Complexity. Time O(1): an allocation and at most two assignments. Space O(1) beyond the new node.
Dry run on 2 <-> 3 <-> 4 with val = 1:
| step | node.prev | node.next | old head’s prev | result |
|---|---|---|---|---|
| create | null |
null |
null |
- |
| forward link | null |
[2] |
null |
1 -> 2 <-> 3 <-> 4, broken backward |
| backward link | null |
[2] |
[1] |
1 <-> 2 <-> 3 <-> 4 |
| return | - | - | - | head is [1] |
After step 2 the list is reachable forward but not well formed: walking backward from the tail would stop at 2 without ever reaching 1. Step 3 restores the invariant. The test file verifies all three facts at once for a concrete pair of nodes: fresh.next == old, old.prev == fresh, and fresh.prev == null.
The caller must store the result, head = insertBeforeHead(head, 1);, since Java cannot reassign the caller’s variable from inside the method. Repeated insertion before the head reverses the insertion order: inserting 1, 2, 3, 4 gives 4 <-> 3 <-> 2 <-> 1.
Approach 2: Insert before any node you hold
Intuition. The head case is a special case of a more general O(1) operation that only a doubly linked list offers: insert before a node given just a reference to it. The prev link tells us the predecessor without a scan. The new node sits between before = target.prev and target, so four links must be set: the new node’s two, target.prev, and before.next. When before is null, target was the head and the new node becomes the new head.
Algorithm.
- Let
before = target.prev. - Create
node = new Node(val, before, target); its links are already correct. - Set
target.prev = node. - If
before != null, setbefore.next = node; otherwisenodeis the new head. - Return the (possibly new) head.
static Node insertBeforeNode(Node head, Node target, int val) {
Node before = target.prev;
Node node = new Node(val, before, target); // prev = before, next = target
target.prev = node;
if (before != null) before.next = node; // splice in between
else head = node; // target was the head, so node is the new head
return head;
}
Complexity. Time O(1), space O(1). With target == head, this method does exactly what Approach 1 does, and the tests run it against the same expected output on every non-empty input.
Compare this with a singly linked list, where “insert before this node” costs O(n): you would have to walk from the head to find the predecessor. That one extra reference per node is what buys the constant-time edit.
Generalisation: insert before the k-th node
Intuition. Insertion before the head is k = 1 of inserting before the k-th node (1-based). For larger k, we walk to the k-th node and hand it to Approach 2. There is no need to stop one node early, as a singly linked list would require, because prev is available at the target.
Algorithm.
- If
k < 1, the position does not exist; return the list unchanged. - If
k == 1, returninsertBeforeHead(head, val). - Walk
targetfrom the head fork - 1steps. If it becomesnull, there are fewer thanknodes; return the head unchanged. - Return
insertBeforeNode(head, target, val).
static Node insertBeforePosition(Node head, int k, int val) {
if (k < 1) return head; // no such position
if (k == 1) return insertBeforeHead(head, val);
Node target = head;
for (int i = 1; i < k && target != null; i++) target = target.next; // walk to the k-th node
if (target == null) return head; // fewer than k nodes
return insertBeforeNode(head, target, val);
}
Complexity. Time O(k), space O(1).
Two boundary behaviours deserve attention. k = 1 always means “become the new head”, so it works even on an empty list and produces a one-node list. For k ≥ 2, the k-th node must exist: on a list of n nodes, k = n + 1 walks off the end and the list is returned unchanged, because there is no node to insert before. If you need “append”, that is a different operation, shown next. The tests cover k = 1 on an empty list, k = n + 1 and k = n + 2 on non-empty lists, and k = 0.
Insertion at the tail, for contrast
Intuition. Inserting at the tail requires reaching the tail, and with only a head reference that is a full walk. Once there, the same two-link pattern applies: the tail’s next points at the new node and the new node’s prev points at the tail.
Algorithm.
- Create the node. If the list is empty, it is the new head.
- Walk
tailuntiltail.next == null. - Set
tail.next = nodeandnode.prev = tail; return the unchanged head.
static Node insertAtTail(Node head, int val) {
Node node = new Node(val);
if (head == null) return node;
Node tail = head;
while (tail.next != null) tail = tail.next;
tail.next = node;
node.prev = tail;
return head;
}
Complexity. Time O(n), space O(1). Keeping a separate tail reference makes it O(1), which is what deque and LRU-cache implementations do.
Edge cases and pitfalls
- Empty list.
head == null. Approach 1 must skiphead.prev = nodeor it throws aNullPointerException; the result is a single node with both linksnull. - Forgetting the backward link. Setting only
node.next = headyields a list that reads correctly forward and is corrupt backward. The test harness walks every result backward and runs a well-formedness check so this cannot slip through. - The new head’s
prev. It must benull. The one-argument constructor guarantees it; if you reuse a node object, reset it explicitly. - Dropped return value.
insertBeforeHead(head, 1);without assignment leaves the caller pointing at the old head, which now has a predecessor it does not know about. Always reassign. - Order of link updates in Approach 2. Read
target.prevbefore overwriting it. The code stores it inbeforefirst. - Positions.
k = 1works on any list including the empty one;k ≥ 2requires thek-th node to exist. Decide whetherk = n + 1should append or be rejected and test it explicitly. - Equal values. The new node is distinct regardless of value;
9inserted before9gives two nodes.
Related problems
- Insertion at the head of Linked List is the singly linked version with one link instead of two.
- Delete head of Doubly Linked List is the inverse operation.
- Introduction to Doubly LL explains the node type and the mirror invariant.
- Reverse a Doubly Linked List rewires both links of every node.
- Find Pairs with Given Sum in Doubly Linked List relies on the
prevlinks being correct for a backward pointer.