Merge Two Sorted Lists
Weave the two lists together, always taking the smaller head.
Approach
Use a dummy head to simplify edge cases. Repeatedly attach the smaller of the two current nodes and advance that list. When one list runs out, link the remainder of the other. The dummy's next is the merged head.
Time complexity
O(n + m)
Space complexity
O(1)
Common mistake
Not using a dummy node, which forces awkward special-casing of the first element.
Interactive walkthrough — step through it below, free and without an account
Problem
Given the heads of two singly linked lists whose values are already sorted in nondecreasing order, combine them into one sorted linked list. Reuse the existing nodes rather than creating a separate collection, and return the head of the combined list.
Examples
Input: list1 = [1,2,4], list2 = [1,3,4]
Output: [1,1,2,3,4,4]
Input: list1 = [], list2 = [0]
Output: [0]
Problem
Problem
Given the heads of two singly linked lists whose values are already sorted in nondecreasing order, combine them into one sorted linked list. Reuse the existing nodes rather than creating a separate collection, and return the head of the combined list.
Problem and Goal
Learning goal: Recognize the merge operation as a controlled weave of two already sorted linked lists.
Core idea Each list exposes a current head node. Because both lists are sorted, the smallest node not yet used must be one of those two heads. Choose the smaller head, append it to the result, and advance only the list that supplied it. This avoids scanning values that cannot be the next answer.
Algorithm plan
- Keep pointers
aandbat the current nodes of the two lists. - Compare
a.valandb.valwhile both pointers are non-null. - Attach the smaller node to the result and advance its source pointer.
- Attach whichever remainder is still non-null.
Micro-example
For [1,2,4] and [1,3,4], the first comparison is 1 <= 1. Take the first list's node, then compare 2 with 1.
Pitfalls
- Treating the lists as unsorted can lead to unnecessary work.
- Advancing both pointers after one choice skips a node.
- Forgetting the remainder loses the tail of one list.
Recap
- The next answer is always the smaller visible head.
- Only one input pointer advances per merge step.
- The operation can reuse existing nodes.
Problem and Goal
Choose the smaller head
At every moment, the next node in sorted order is the smaller of the two current heads.
Compare only two nodes, attach one, and advance exactly one list.
Why the Dummy Head Matters
Learning goal: Understand how a dummy node removes special cases when building the merged list.
Core idea
A result list normally needs special handling for its first node because there is no previous node to update. Create a temporary dummy node first, then always attach the chosen node after tail. The first real node becomes dummy.next, whether it came from the first list, the second list, or the only non-empty list.
Data structure model
dummy: a fixed sentinel before the real answer.tail: the last node currently in the merged chain.a,b: the unmerged suffixes of the two inputs.- After attaching a node, set
tail = tail.next.
Uniform pointer update
With a dummy, every insertion is simply tail.next = chosen; no first-node branch is needed.
Micro-example
Initially, dummy -> None and tail = dummy. If list1 contributes 1, the update is dummy.next = 1, then tail moves to that 1.
Pitfalls
- Returning
dummyincludes the sentinel in the answer. - Reusing
tailas an input pointer can disconnect the result. - Creating new nodes is unnecessary for this problem.
Recap
- The dummy is scaffolding, not part of the returned list.
dummy.nextis the merged head.- One invariant makes every append identical.
Why the Dummy Head Matters
`dummy` sits before every real result node, including the first one.
dummy -> 1
Main Algorithm
Learning goal: Translate the merge invariant into a short, constant-space procedure.
Core idea
The merged prefix is always sorted, and tail points to its final node. While both inputs remain, the smaller current value is safe to append because every other unmerged node is at least as large as its own current head. Once either pointer becomes null, the other pointer already names a sorted remainder, so link it directly.
Algorithm plan
Create dummy and set tail = dummy
Set a and b to the two list heads
While a and b are not null:
choose a if a.val <= b.val; otherwise choose b
attach chosen after tail
move tail to chosen
advance the chosen input pointer
Attach a if it remains; otherwise attach b
Return dummy.nextWhy linking the remainder works
The untouched suffix is already sorted, and every node in it is no smaller than the merged prefix's last node.
Micro-example
After producing 1,1,2,3,4, list1 is empty and list2 points to its final 4. One link completes the answer.
Pitfalls
- Use
<=consistently if equal nodes should favor the first list. - Save or identify the chosen node before advancing its input pointer.
- Check both pointers before reading
.val.
Recap
- Compare, attach, advance: one repeated operation.
- Link the remaining suffix in one step.
- The returned chain begins at
dummy.next.
Main Algorithm
| Approach | Time | Extra space | Key trade-off |
|---|---|---|---|
| Dummy + two pointers | O(n + m) | O(1) | Uniform pointer logic |
| Copy values, sort, rebuild | O((n + m) log(n + m)) | O(n + m) | Ignores existing order |
| Repeatedly scan both lists | O(nm) | O(1) | Repeats comparisons |
End-to-End Walkthrough
Learning goal: Trace every pointer and link while merging one concrete pair of lists.
Core idea
Use list1 = [1,2,4] and list2 = [1,3,4]. The visualization treats each value as an existing node and shows the next map changing as links are woven together. Equal values are resolved by taking list1 first, using the <= rule.
Pointer movement
| Step | Comparison | Chosen node | Pointer advanced |
|---|---|---|---|
| 1 | 1 <= 1 | list1 1 | a |
| 2 | 2 > 1 | list2 1 | b |
| 3 | 2 <= 3 | list1 2 | a |
| 4 | 4 > 3 | list2 3 | b |
| 5 | 4 <= 4 | list1 4 | a |
| 6 | remainder | list2 4 | b |
Micro-example
The links evolve as dummy -> 1 -> 1 -> 2 -> 3 -> 4 -> 4. The final result is [1,1,2,3,4,4].
Pitfalls
- A comparison event should attach only one node.
- The old
nextlinks are rewired throughtail; do not duplicate nodes. - The final remainder step is part of the algorithm, not an optional cleanup.
Recap
- Follow the smaller head at each comparison.
- The merged prefix remains sorted after every attachment.
- The visualization ends at
dummy.next.
End-to-End Walkthrough
Step through merging [1,2,4] and [1,3,4] by rewiring existing nodes.
Pitfalls and Edge Cases
Learning goal: Identify the pointer mistakes that most often break linked-list merging.
Core idea The algorithm is short, but linked-list code fails when it mishandles the first node, null pointers, or the final suffix. The dummy head is especially valuable because it eliminates the awkward question, "Is this the first result node?" Every real node follows the same attach-and-advance pattern.
Edge-case checklist
- If both lists are empty,
dummy.nextisnull. - If one list is empty, link the other list immediately.
- If all values in one list are smaller, the other list becomes the remainder.
- Equal values still require advancing exactly one pointer.
Common pitfall
Not using a dummy node forces awkward special-casing of the first element and often creates a separate, error-prone initialization branch.
Micro-example
For [] and [0], the loop never runs. The remainder assignment sets dummy.next to the node containing 0, so the answer is [0].
Pitfalls
- Reading
a.valorb.valafter that pointer becamenull. - Returning the dummy instead of
dummy.next. - Detaching the remainder by overwriting a needed
nextpointer.
Recap
- Let null checks guard every comparison.
- Let the dummy handle the first insertion.
- Link, do not copy, the sorted remainder.
Pitfalls and Edge Cases
Why is returning `dummy` incorrect after the merge?
Complexity and Wrap-up
Learning goal: Explain why dummy-head merging is optimal for the required time and space bounds.
Core idea
Each node is attached once and each input pointer moves only forward. Therefore, the loop performs at most n + m comparisons or attachments, where n and m are the input lengths. The algorithm stores only a few pointers; it does not allocate an array or recursion stack.
Complexity analysis
| Component | Cost | Reason |
|---|---|---|
| Main merge loop | O(n + m) | Each node is visited at most once |
| Remainder link | O(1) | One pointer assignment |
| Auxiliary space | O(1) | Only dummy, tail, a, and b |
Micro-example
For lengths 3 and 3, at most six nodes are processed. The result reuses those same six nodes, so extra storage stays constant.
Pitfalls
- Calling the result construction
O(n + m)space when nodes are reused is incorrect. - Counting the dummy as an input node changes neither bound.
- A recursive implementation may use
O(n + m)call-stack space, unlike this iterative approach.
Recap
- Time:
O(n + m). - Extra space:
O(1). - Dummy plus two moving input pointers gives clean, optimal weaving.
Complexity and Wrap-up
Summarizes the cost of the iterative merge.
Now try it on your own problem
Paste any LeetCode problem — or one of your own — and codebrew builds a walkthrough like this one for it.
Build a lesson from any problem
