Linked List · 28 of 36
Flatten a Linked List
A row of boxes where each box has its own sorted column hanging below it must become one sorted column.
- Simple way
- time O(N log N)
room O(N) - Clever way
- time O(N k)
room O(1)
The problem, simply
Every box has two arrows. next points right, to the next box in the top row. bottom points down, to the next box in its own column. Each column, read top to bottom, is sorted from small to big.
Turn the whole thing into one list that goes only through bottom arrows and is sorted. Return its first box. Here N is the total number of boxes and k is the number of columns.
A story to remember it
Picture it
The top row 5 10 19 28 is joined by next arrows. Each box has a sorted column hanging down from it through bottom arrows, drawn here sideways.
- looking now
Examples
Four columns
- In
5:[5 7 8 30], 10:[10 20], 19:[19 22 50], 28:[28 35 40 45]- Out
5 7 8 10 19 20 22 28 30 35 40 45 50
All 13 boxes in one sorted line through the bottom arrows.
Columns that overlap a lot
- In
10:[10 20 30], 15:[15 25], 12:[12 40]- Out
10 12 15 20 25 30 40
The top row does not need to be sorted for merging to work, only each column.
One column
- In
3:[3 6 9]- Out
3 6 9
Already flat. Nothing to merge.
Columns of one box
- In
4:[4], 1:[1], 2:[2]- Out
1 2 4
Only the top row exists. The merges sort it.
Way 1: the simple way
Throw all the numbers in a basket, sort them, and build a new column.
- Walk the top row with
next. For each box, walk down its column withbottomand copy every number into a list. - Sort the list.
- Build a new chain of boxes linked with
bottomand return its head.
All 13 numbers copied out of the columns, then sorted. The original boxes are not reused.
- just added
class Solution {
Node flatten(Node root) {
List<Integer> all = new ArrayList<>();
for (Node col = root; col != null; col = col.next)
for (Node p = col; p != null; p = p.bottom)
all.add(p.data); // copy every number
Collections.sort(all);
Node dummy = new Node(0), tail = dummy;
for (int v : all) {
tail.bottom = new Node(v); // new boxes, linked downwards
tail = tail.bottom;
}
return dummy.bottom;
}
}How long it takes: copying is O(N) and sorting is O(N log N). Extra room: the copy and the new boxes, O(N). It ignores that every column is already sorted.
Way 2: the clever way
Every column is already sorted, so use the card pile trick: merging two sorted lists is easy and needs no extra room. Keep an answer, and merge each column into it, one after another.
- The answer starts as the first column. Remember the next column, then cut the
nextarrow. - For each following column: merge the answer and the column through their
bottomarrows. Two fingers, always take the smaller box, then glue on whatever is left. - Return the answer.
Merge one column at a time into the answer
01 / 13
Each column is a sorted list joined by bottom arrows. Start with the first column, 5 7 8 30, as the answer so far. Cut it loose from the next arrow.
- columns merged
- 1
- checking
- done
- answer
- just added
- ignored
class Solution {
Node flatten(Node root) {
if (root == null) return null;
Node answer = root;
Node col = root.next;
answer.next = null;
while (col != null) {
Node nextCol = col.next; // remember the rest of the top row
col.next = null;
answer = merge(answer, col); // merge one column into the answer
col = nextCol;
}
return answer;
}
private Node merge(Node a, Node b) {
Node dummy = new Node(0), tail = dummy;
while (a != null && b != null) {
if (a.data <= b.data) { tail.bottom = a; a = a.bottom; }
else { tail.bottom = b; b = b.bottom; }
tail = tail.bottom;
}
tail.bottom = (a != null) ? a : b; // glue on the rest
return dummy.bottom;
}
}How long it takes: the answer grows with every merge and is walked again each time. With k columns that is at most O(N k) steps. Extra room: a few fingers, O(1). The original boxes are reused, only arrows change.
Many solutions write the same thing with recursion: flatten everything to the right first, then merge this column with the result. That is the same work but uses O(k) stack.
Watch out for
- Losing the rest of the top row. Save
col.nextbefore you merge or change anything. - Using
<versus<=does not change correctness, but<=keeps equal values in their original order, which is nice. - An empty input. Return
null. - Some judges print the answer through
bottomonly. Make sure the last box'sbottomisnull, which the merge's glue step gives you for free.
What interviewers ask next
- Can you make it faster? Yes. Merge the columns in pairs like a tournament, or use a min heap of the column tops. Both give
O(N log k), the same as mergingksorted lists. - Flatten a multilevel doubly linked list (LeetCode 430). There the child lists are not sorted and you splice them in place, in depth first order, instead of merging.
- Flatten a binary tree to a linked list. Again no sorting: put the left subtree between a node and its right subtree.
- Why does
O(N k)happen? The first column gets walked in every merge. If columns are equal size, the work is aboutN k / 2.
Remember this
Grown-up words: the card piles are sorted linked lists, putting the smaller top card down is the merge step of merge sort, the top row is the next chain, the columns are bottom chains, and the tournament is divide and conquer merging.