The O(n) Club: Merge k Sorted Lists—How to Outsmart Brute Force (and Your Past Self)
⚡ TL;DR
Why manually stitch k sorted linked lists together when you can have Java’s PriorityQueue do it? Toss the head of each list into the heap, then always pop the smallest. Rinse, repeat, relish the O(N log k):
// Java: prioritizing your sanity PriorityQueue<ListNode> pq = new PriorityQueue<>((a, b) -> a.val - b.val); for (ListNode head : lists) if (head != null) pq.add(head); ListNode dummy = new ListNode(-1), tail = dummy; while (!pq.isEmpty()) { ListNode node = pq.poll(); tail.next = node; tail = tail.next; if (node.next != null) pq.add(node.next); } return dummy.next;
🧠 How Devs Usually Mess This Up
If your instinct is to merge lists one by one (as if punishing yourself for picking comp sci over art school), you’re not alone. Brute-forcing left-to-right is a great way to turn O(N log k) into O(kN)—wonderful for wasting CPU and your patience. Or, you might feel tempted to reconstruct every node into a new list, making every memory allocator in your system cry. Bonus fail points if you overlook empty lists and end up with a NullPointerException so dramatic the JVM tries to resign.
🔍 What’s Actually Going On
Imagine you’re a chef serving from k buffet lines. Each line is sorted—no, you don’t shove all of Line 1 onto the plate, then Line 2, then Line 3. Instead, you scan all the lines and always pick the tastiest (smallest value) morsel next, until every buffet is empty. The Java PriorityQueue is your cunning sous-chef—always handing you the next bite until all lists are devoured, and you only touched existing ingredients (nodes). Perfect for server logs, event streams, or feeling like a merge-sort master.
🛠️ PseudoCode
- Create a PriorityQueue: Inserts heads from every non-empty list.
PriorityQueue<ListNode> pq = ...
- Initialize your merged list’s dummy start: Keeps code neat and your pointer safe.
ListNode dummy = new ListNode(-1);
- Add the head node of each list: Skip nulls like unwanted RSVP emails.
for (ListNode head : lists) if (head != null) pq.add(head);
- While the PriorityQueue isn’t empty:
- Pop the smallest node from pq (
pq.poll()
). - Attach to merged list:
tail.next = node
. - Advance your tail pointer.
- If the node has a next, add it to pq.
- Pop the smallest node from pq (
- Return dummy.next (the merged beauty).