23. Merge K Sorted Lists
hardAsked at GustoMerge k sorted linked lists into one. Gusto asks this as the natural escalation from Merge Two Sorted Lists — they want to see the divide-and-conquer approach or a min-heap, and they care that you recognise the O(Nk) naive solution is unacceptable.
By Alex Chen, Founder, InterviewChamp.AI · Last verified
Source citations
Public interview reports confirming this problem appears in Gusto loops.
- Glassdoor (2025-12)— Reported as a Gusto senior SWE onsite problem paired with Merge Two Sorted Lists earlier in the interview.
- Blind (2025-10)— Gusto threads cite Merge K Sorted Lists as a hard problem that tests both heap reasoning and divide-and-conquer.
Problem
You are given an array of k linked-lists lists, each linked list is sorted in ascending order. Merge all the linked lists into one sorted linked list and return it.
Constraints
k == lists.length0 <= k <= 10^40 <= lists[i].length <= 500−10^4 <= lists[i][j] <= 10^4lists[i] is sorted in ascending order.The sum of lists[i].length will not exceed 10^4.
Examples
Example 1
lists = [[1,4,5],[1,3,4],[2,6]][1,1,2,3,4,4,5,6]Explanation: All three sorted lists are merged into one globally sorted list.
Example 2
lists = [][]Explanation: Empty input — return null.
Approaches
1. Divide and conquer (pairwise merge)
Repeatedly pair lists and merge each pair, halving the number of lists each round. Like a merge-sort tree — O(log k) rounds, each round O(N) total.
- Time
- O(N log k) where N is total nodes
- Space
- O(log k) for recursion
function mergeKLists(lists) {
if (!lists || lists.length === 0) return null;
return mergeRange(lists, 0, lists.length - 1);
}
function mergeRange(lists, left, right) {
if (left === right) return lists[left];
const mid = Math.floor((left + right) / 2);
const l1 = mergeRange(lists, left, mid);
const l2 = mergeRange(lists, mid + 1, right);
return mergeTwoLists(l1, l2);
}
function mergeTwoLists(l1, l2) {
const dummy = { val: -1, next: null };
let tail = dummy;
while (l1 && l2) {
if (l1.val <= l2.val) { tail.next = l1; l1 = l1.next; }
else { tail.next = l2; l2 = l2.next; }
tail = tail.next;
}
tail.next = l1 ?? l2;
return dummy.next;
}Tradeoff: O(N log k) time, O(log k) stack space. No external heap needed. Builds on the two-list merge as a subroutine — elegant if you already implemented Merge Two Sorted Lists.
2. Min-heap (priority queue)
Push all list heads into a min-heap. Repeatedly extract the minimum, append to result, and push that node's next into the heap.
- Time
- O(N log k)
- Space
- O(k) for the heap
// JS has no built-in heap — sketch with sorted array as a stand-in
// In a real interview, use a MinPriorityQueue or implement a heap.
function mergeKLists(lists) {
// Simulate heap with sorted insertion (educational — real heap preferred)
let heap = [];
for (const head of lists) {
if (head) heap.push(head);
}
heap.sort((a, b) => a.val - b.val); // initial heapify
const dummy = { val: -1, next: null };
let tail = dummy;
while (heap.length) {
const node = heap.shift(); // extract min
tail.next = node;
tail = tail.next;
if (node.next) {
// insert next and maintain sort (O(k) per step in this sketch)
const insertIdx = heap.findIndex(n => n.val > node.next.val);
insertIdx === -1 ? heap.push(node.next) : heap.splice(insertIdx, 0, node.next);
}
}
return dummy.next;
}Tradeoff: With a proper binary heap: O(N log k) time, O(k) space. The sketch above uses sorted array insertion (O(k) per step = O(Nk) total) — always mention this limitation and that a real heap is needed. In a production JS context, use a library or implement a MinHeap class.
Gusto-specific tips
Lead with the divide-and-conquer approach since it builds directly on Merge Two Sorted Lists (which Gusto may have asked earlier in the same session). Explicitly state why the naive approach — merging one list at a time from left to right — is O(Nk) and unacceptable. If the interviewer asks for the heap approach, note that JS lacks a built-in min-heap and sketch the API you'd want.
Common mistakes
- Merging lists sequentially left-to-right — O(Nk) time, not O(N log k).
- Forgetting to handle the empty lists array — return null immediately.
- In the heap approach, not pushing the next node after extraction — the heap will drain early.
- Off-by-one in the divide step — ensure left and right boundaries are tracked correctly to avoid infinite recursion.
Follow-up questions
An interviewer at Gusto may pivot to one of these next:
- What if each list is very long (1 million nodes) but k is small (3 lists)? Does your approach change?
- How would you merge k sorted arrays rather than linked lists?
- What is the memory usage comparison between divide-and-conquer and heap approaches?
Solve it now
Free. No sign-up. Python and JavaScript run instantly in your browser.
FAQ
Why is divide-and-conquer O(N log k) not O(N log N)?
Each node is touched once per merge level. There are log k levels (halving k lists each round). Total work = N × log k, not N × log N.
Which approach is better in practice?
Both are O(N log k). Divide-and-conquer avoids external data structures and is cache-friendly. The heap approach is more natural if k changes dynamically (e.g., streaming input).
Does JavaScript have a built-in min-heap?
No. You'd either implement one or use a library. In an interview, describe the heap API you'd use (insert, extractMin) and state the complexity, then implement divide-and-conquer as the code.
Practice these live with InterviewChamp.AI
Drill Merge K Sorted Lists and other Gusto interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.
Practice these live with InterviewChamp.AI →