Linked List · 31 of 36
Merge K sorted Linked list
Several sorted chains must become one sorted chain, by always picking the smallest box from the fronts of all the chains.
- Simple way
- time O(N k)
room O(1) - Clever way
- time O(N log k)
room O(k)
The problem, simply
You get k linked lists. Each one is sorted from small to big. Join all their boxes into one sorted list and return its head. Reuse the boxes you were given and only rewire the arrows.
Call the total number of boxes in all lists N.
A story to remember it
Picture it
Three sorted lists. The smallest box overall must be one of the three front boxes, 1, 1 and 2.
- checking
Examples
A normal case
- In
[1 4 5], [1 3 4], [2 6]- Out
1 1 2 3 4 4 5 6
All eight boxes in sorted order. Equal values can come in either order.
Some lists are empty
- In
[], [3], [], [1 2]- Out
1 2 3
Empty lists are skipped. Never put a null into the heap.
No lists
- In
[]- Out
null
Nothing to merge. Return an empty list.
One list
- In
[5 7 9]- Out
5 7 9
It is already sorted, so the answer is the same list.
Way 1: the simple way
Merge the lists one at a time, using the merge step from merge sort.
- Start with the answer equal to the first list.
- Merge the answer with the second list. Then merge that with the third list, and so on.
- Each merge walks two sorted lists with two fingers and always takes the smaller front box.
After merging a and b, the answer has 6 boxes. To add list c, we walk all 6 again. The answer gets longer every round, and we keep walking it.
- checking
- seen
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
ListNode answer = null;
for (ListNode list : lists) answer = mergeTwo(answer, list); // one at a time
return answer;
}
private ListNode mergeTwo(ListNode a, ListNode b) {
ListNode dummy = new ListNode(0), tail = dummy;
while (a != null && b != null) {
if (a.val <= b.val) { tail.next = a; a = a.next; }
else { tail.next = b; b = b.next; }
tail = tail.next;
}
tail.next = (a != null) ? a : b; // glue on whatever is left
return dummy.next;
}
}How long it takes: the answer grows each round and is walked again each round. With k lists of equal size that adds up to about N * k / 2 steps, O(N k). Extra room: a few fingers, O(1).
Way 2: the clever way
Use the lunch lady's podium: a min heap, also called a priority queue. It holds at most one box from each list, and it hands you the smallest one in O(log k).
- Put the head of every non empty list into the heap.
- Take the smallest box out of the heap and hang it at the end of the answer.
- If that box has a next box, put the next box into the heap.
- Repeat until the heap is empty.
The heap always hands over the smallest front box
01 / 10
Put the front box of every list into a min heap. The heap always keeps the smallest box on top. It holds 3 boxes, one per list.
- looking now
- checking
- done
- answer
- just added
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<ListNode> heap = new PriorityQueue<>((x, y) -> Integer.compare(x.val, y.val));
for (ListNode head : lists) if (head != null) heap.offer(head); // the front of each line
ListNode dummy = new ListNode(0), tail = dummy;
while (!heap.isEmpty()) {
ListNode smallest = heap.poll(); // youngest front kid
tail.next = smallest;
tail = smallest;
if (smallest.next != null) heap.offer(smallest.next); // the next kid steps up
}
return dummy.next;
}
}How long it takes: each of the N boxes goes into the heap once and out once, and each heap step costs O(log k), so O(N log k). Extra room: the heap holds at most k boxes, O(k).
Another way: merge in pairs
There is a way with the same speed and no heap. Merge list 1 with list 2, list 3 with list 4, and so on. Now there are k / 2 lists. Do it again, and again, until one list is left. Like a sports tournament, each round halves the number of lists, so there are log k rounds, and each round walks all N boxes once.
Round 1 merges a with b. c has no partner, so it waits. Round 2 merges the two lists that are left. Two rounds for three lists.
- done
- answer
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if (lists.length == 0) return null;
for (int gap = 1; gap < lists.length; gap *= 2) { // one tournament round
for (int i = 0; i + gap < lists.length; i += 2 * gap) {
lists[i] = mergeTwo(lists[i], lists[i + gap]);
}
}
return lists[0];
}
private ListNode mergeTwo(ListNode a, ListNode b) {
ListNode dummy = new ListNode(0), tail = dummy;
while (a != null && b != null) {
if (a.val <= b.val) { tail.next = a; a = a.next; }
else { tail.next = b; b = b.next; }
tail = tail.next;
}
tail.next = (a != null) ? a : b;
return dummy.next;
}
}This is O(N log k) time and O(1) extra room, since it changes the lists array in place.
Watch out for
- Comparing with
x.val - y.val. With very large or very negative values the subtraction overflows. UseInteger.compare. - Putting all
Nboxes into the heap at once. That works but costsO(N log N)time andO(N)room. Keep only one box per list. - Forgetting the dummy box, then special casing the very first answer box.
- Returning
dummyinstead ofdummy.next.
What interviewers ask next
- Why is the heap
log kand notlog N? It never holds more than one box per list, so its size is at mostk. - Which is better, the heap or merging in pairs? Same
O(N log k)time. The pairs version needs no extra room and has smaller constants. The heap version works when lists arrive as streams and you cannot see them all at once. - What if the inputs are arrays instead of lists? Same heap, but store (value, which array, index) in it.
- Find the smallest range that has at least one number from each of
ksorted lists (LeetCode 632). Same heap of front boxes, plus track the biggest front value. - Flatten a linked list with
bottompointers. It is the same problem: every column is a sorted list to merge.
Remember this
Grown-up words: the lunch lines are sorted linked lists, the podium is a min heap or priority queue, the youngest on top is the heap's minimum, the tournament is divide and conquer, and mergeTwo is the merge step of merge sort.