Linked Lists · Linked List Fundamentals

Linked List Merge Technique

Building a single sorted list out of two sorted lists by repeatedly taking the smaller of the two current heads, using a dummy node to avoid special-casing the very first link.

  • 5 min read
  • 11 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: merging k lists by pairing them up
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Merging sorted linked lists means comparing the current heads again and again, and linking the smaller one onto the result. No new nodes are made; it's all rewiring, in one pass. With two lists, that's the merge step of merge sort. With k lists, the question is how to combine them. Folding them in one at a time is slow. Pairing them up round by round, or keeping the k heads in a min-heap, brings it down to O(N log k).

We'll go from simple to more technical:

  1. The pattern: a dummy head, a tail pointer, take the smaller head.
  2. A second flavor: merging k lists by pairing them up.
  3. Where merging sorted streams runs in production.
  4. The bugs that drop nodes - a forgotten leftover, an unpaired list.

The approach

Start the result with a dummy node and keep tail, its last node. Each round, compare the two lists' current heads. Link the smaller one after tail, and move along that list. When one list runs out, the other's remaining nodes are already sorted and all larger, so link the whole rest at once.

Merging 2 -> 5 -> 9 with 3 -> 4 -> 10:

First list's headSecond list's headTakeResult so far
2322
5332 -> 3
5442 -> 3 -> 4
51052 -> 3 -> 4 -> 5
91092 -> 3 -> 4 -> 5 -> 9
(empty)10the rest2 -> 3 -> 4 -> 5 -> 9 -> 10

Java

ListNode dummy = new ListNode(0);
ListNode tail = dummy;
while (list1 != null && list2 != null) {
    if (list1.val <= list2.val) { tail.next = list1; list1 = list1.next; }
    else                        { tail.next = list2; list2 = list2.next; }
    tail = tail.next;
}
tail.next = (list1 != null) ? list1 : list2;   // attach whichever list still has leftovers
return dummy.next;

Python

def merge_two(list1: ListNode | None, list2: ListNode | None) -> ListNode | None:
    dummy = ListNode(0)
    tail = dummy
    while list1 is not None and list2 is not None:
        if list1.val <= list2.val:
            tail.next, list1 = list1, list1.next
        else:
            tail.next, list2 = list2, list2.next
        tail = tail.next
    tail.next = list1 if list1 is not None else list2   # attach whichever list still has leftovers
    return dummy.next

The dummy node keeps the loop simple. Without it, adding the very first node would need its own branch. At the end, dummy.next is the real head, whichever list it came from.

A second flavor: merging k lists by pairing them up

You could merge k lists one at a time into a growing result. But every merge rescans that growing result, so the total is O(N*k). Instead, merge the lists in pairs, using the two-list merge above. Each round halves the number of lists:

Java

List<ListNode> current = new ArrayList<>(Arrays.asList(lists));
while (current.size() > 1) {
    List<ListNode> next = new ArrayList<>();
    for (int i = 0; i < current.size(); i += 2) {
        ListNode a = current.get(i);
        ListNode b = (i + 1 < current.size()) ? current.get(i + 1) : null;   // odd one out: no partner
        next.add(mergeTwo(a, b));
    }
    current = next;
}
return current.get(0);

Python

def merge_k(lists: list[ListNode | None]) -> ListNode | None:
    current = list(lists)
    while len(current) > 1:
        nxt = []
        for i in range(0, len(current), 2):
            a = current[i]
            b = current[i + 1] if i + 1 < len(current) else None   # odd one out: no partner
            nxt.append(merge_two(a, b))
        current = nxt
    return current[0]

With 5 lists, the first round merges two pairs and carries the fifth list forward, leaving 3. The next rounds leave 2, then 1. Each round touches every node once, and there are about log k rounds, so the total is O(N log k). The list without a partner must be carried forward, not skipped. Merging it with null returns it unchanged.

Where you'll see it

This is the merge step of merge sort. It's also how two sorted streams, such as two pages of database results or two sorted log files, become one sorted stream without loading either fully. A log aggregator merging sorted logs from many servers usually keeps a min-heap of the current heads instead (see K-Way Merge Heap in Heaps). That's also O(N log k), in a single pass.

Which tool for which job?

You needUse
Merge two sorted listsdummy head + tail, take the smaller, attach the leftover
Merge k lists, all in memorypair them up round by round, O(N log k)
Merge k streams as they arrivea min-heap of the k current heads, O(N log k)
Keep equal keys in input ordertake from the first list on ties (<=)
Keep the inputs unchangedcopy nodes instead of relinking (O(N) extra)
A few small listsfolding one at a time is fine

Common mistakes

MistakeWhat happensDo this instead
No tail.next = a or b after the loopthe rest of one list is lostattach the leftover - it's already sorted
Dropping the odd list in a pairing rounda whole list disappearscarry it to the next round
Folding k lists into one growing listO(N k)pair up, or use a heap
Pushing nodes into a Python heap on their ownTypeError on equal valuespush (value, index, node)
< instead of <=equal keys swap order (unstable)<= takes the first list's on ties

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. When one list runs out, why is one assignment enough to attach the other, instead of walking it node by node?

Show answer

The remaining nodes are already sorted among themselves. Each of them is also larger than everything already in the result. Linking the whole chain with one assignment gives the same sorted result as appending them one by one.

2. In a pairing round with an odd number of lists, why merge the last list with null instead of skipping it?

Show answer

Merging a list with null returns it unchanged, which carries it into the next round. A loop that only handles complete pairs drops that list, and its values vanish from the result. Test with an odd number of lists to catch it.

3. Why is pairing up O(N log k) while folding one at a time is O(N*k)?

Show answer

Folding rescans the growing result on every merge. By the end it holds nearly all N nodes, and it's rescanned about k times. Pairing up takes about log k rounds, and each round touches every node once.

Key takeaways

  • Merge two sorted lists in one pass: compare the heads and link the smaller one. A dummy node removes the first-node special case.
  • When one list runs out, attach the rest of the other with one assignment.
  • Merge k lists by pairing them up round by round: O(N log k), not O(N*k).
  • Carry an unpaired list into the next round. Test with an odd number of lists.

Quick revise

The whole skill on one page, for the night before an interview.

Everything from the lesson and the deep dive on one page. Skim it before practice or an interview, and open the full lesson for anything that doesn't ring a bell.

Pick the tool

You needUse
Two listsdummy + tail, take the smaller
k lists in memorypair up, O(N log k)
k streamsa heap of heads, O(N log k), O(k) memory
Stable ties<=

Snippets worth knowing by heart

Java

ListNode dummy = new ListNode(0), tail = dummy;
while (a != null && b != null) {
    if (a.val <= b.val) { tail.next = a; a = a.next; }   // <= keeps it stable
    else { tail.next = b; b = b.next; }
    tail = tail.next;
}
tail.next = (a != null) ? a : b;                         // the leftover is already sorted
return dummy.next;

Python

heap = [(node.val, i, node) for i, node in enumerate(lists) if node]   # i breaks ties
heapq.heapify(heap)
dummy = tail = ListNode(0)
while heap:
    _, i, node = heapq.heappop(heap)
    tail.next = tail = node
    if node.next:
        heapq.heappush(heap, (node.next.val, i, node.next))
return dummy.next

The traps

  • No leftover attach: the rest of one list is lost. Attach it after the loop; it's already sorted.
  • Dropping the odd list: in a pairing round a whole list disappears. Carry it to the next round.
  • Folding k lists into one growing list is O(N k). Merge in pairs, or use a heap.
  • Heap ties comparing nodes: Python raises TypeError on equal values. Push (value, index, node).
  • < is unstable: equal keys swap order. Use <= to take the first list's on ties.

Staff-level points, one line each

  • 64 lists of 100: folding 205,400 comparisons, pairing 38,273, heap 52,394.
  • Heap = O(k) memory, ideal for streams (external sort).
  • <= gave 1a, 2a, 2b, 3b; < gave 1a, 2b, 2a, 3b.
  • heapq with (value, node) raises TypeError on ties - add an index.
  • Relinking consumes the inputs; copy if the caller needs them.

Say it in an interview

  • "I compare the two heads, link the smaller, and attach whatever is left."
  • "For k lists I pair them up, so each node is merged log k times."
  • "If they were streams, I'd keep a min-heap of the current heads."
  • "Taking from the first list on ties keeps the merge stable."

Practice

This skill comes with 11 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Linked List Merge Technique free

Merge K Sorted Lists

HardProCoding problemJava · Python

You are given an array of k sorted linked list heads (any of them may be empty/null). Merge all of them into one sorted linked list and return its head.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Linked List Merge Technique free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime