23. Merge K Sorted Lists
hardAsked at CohereMerge k sorted linked lists into one sorted list. Cohere asks this because k-way merge is the core algorithm for combining ranked results from multiple retrieval shards in a distributed RAG system — minimising latency while maintaining global ordering.
By Alex Chen, Founder, InterviewChamp.AI · Last verified
Source citations
Public interview reports confirming this problem appears in Cohere loops.
- Glassdoor (2026-Q1)— Cohere SWE and MLE onsite reports list Merge K Sorted Lists as a hard problem with a retrieval-systems framing.
- Blind (2025-12)— Cohere Blind threads recommend practising this as a high-signal hard problem combining heaps and linked lists.
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 lists merged in sorted order.
Example 2
lists = [][]Explanation: Empty input.
Example 3
lists = [[]][]Explanation: Single empty list.
Approaches
1. Min-heap
Push the head node of each non-null list into a min-heap keyed by node value. Repeatedly extract the minimum, append it to the result, and push its next node into the heap.
- Time
- O(N log k) where N = total nodes
- Space
- O(k)
// Min-heap implementation (JS has no built-in heap)
class MinHeap {
constructor() { this.heap = []; }
push(node) {
this.heap.push(node);
this._bubbleUp(this.heap.length - 1);
}
pop() {
const min = this.heap[0];
const last = this.heap.pop();
if (this.heap.length) { this.heap[0] = last; this._siftDown(0); }
return min;
}
_bubbleUp(i) {
while (i > 0) {
const p = (i - 1) >> 1;
if (this.heap[p].val <= this.heap[i].val) break;
[this.heap[p], this.heap[i]] = [this.heap[i], this.heap[p]];
i = p;
}
}
_siftDown(i) {
const n = this.heap.length;
while (true) {
let min = i, l = 2*i+1, r = 2*i+2;
if (l < n && this.heap[l].val < this.heap[min].val) min = l;
if (r < n && this.heap[r].val < this.heap[min].val) min = r;
if (min === i) break;
[this.heap[min], this.heap[i]] = [this.heap[i], this.heap[min]];
i = min;
}
}
get size() { return this.heap.length; }
}
function mergeKLists(lists) {
const heap = new MinHeap();
for (const head of lists) if (head) heap.push(head);
const dummy = { val: 0, next: null };
let curr = dummy;
while (heap.size) {
const node = heap.pop();
curr.next = node;
curr = curr.next;
if (node.next) heap.push(node.next);
}
return dummy.next;
}Tradeoff: O(N log k) — optimal. Heap always holds at most k nodes. This is the production-grade approach for merging k retrieval shards.
2. Divide and conquer (pairwise merge)
Repeatedly pair up lists and merge each pair using the two-list merge subroutine. Each round halves the number of lists in O(log k) rounds, each round processing all N nodes.
- Time
- O(N log k)
- Space
- O(log k) call stack
function mergeTwo(l1, l2) {
const dummy = { val: 0, next: null };
let curr = dummy;
while (l1 && l2) {
if (l1.val <= l2.val) { curr.next = l1; l1 = l1.next; }
else { curr.next = l2; l2 = l2.next; }
curr = curr.next;
}
curr.next = l1 || l2;
return dummy.next;
}
function mergeKLists(lists) {
if (!lists.length) return null;
let interval = 1;
while (interval < lists.length) {
for (let i = 0; i + interval < lists.length; i += interval * 2) {
lists[i] = mergeTwo(lists[i], lists[i + interval]);
}
interval *= 2;
}
return lists[0];
}Tradeoff: Same O(N log k) complexity, no heap required. Easier to implement correctly in JS since there is no built-in priority queue. Discuss both and let the interviewer choose.
Cohere-specific tips
Cohere retrieval systems merge candidate lists from multiple embedding index shards. Frame your answer: 'Each retrieval shard returns its top-k chunks sorted by similarity score — merging k sorted lists is exactly the algorithm that produces the globally-ranked candidate set for reranking.' Mention that the heap approach mirrors real distributed merge systems (e.g., top-k aggregation in search engines). Be ready to write the MinHeap from scratch in JS since there is no built-in.
Common mistakes
- Not pushing the next node after extracting from the heap — the heap must be fed continuously.
- Initialising the heap with all N nodes instead of just the k heads — defeats the O(k) space advantage.
- In the divide-and-conquer approach, incorrect interval doubling — use i += interval * 2 to advance past processed pairs.
- Forgetting to handle empty lists and null heads — guard every heap.push with a null check.
Follow-up questions
An interviewer at Cohere may pivot to one of these next:
- Find the K-th Smallest Element Among K Sorted Arrays — heap with array-index bookkeeping.
- Smallest Range Covering Elements from K Lists (LC 632) — sliding window of heap min/max.
- How would you merge k sorted document-chunk streams in a distributed retrieval system?
Solve it now
Free. No sign-up. Python and JavaScript run instantly in your browser.
FAQ
Why O(N log k) and not O(N log N)?
The heap holds at most k elements at any time. Each of the N insertions and extractions costs O(log k), giving O(N log k) total — much better than O(N log N) sort-all.
Which approach is easier to implement under time pressure?
Divide-and-conquer, because it only requires the two-list merge subroutine. The heap approach requires a correct MinHeap implementation in JS, which adds ~25 lines of code.
How would you parallelise this?
The divide-and-conquer approach is naturally parallel: each pair of lists can be merged independently in each round. Distribute the pairs across workers and reduce in O(log k) rounds — similar to a parallel merge sort.
Practice these live with InterviewChamp.AI
Drill Merge K Sorted Lists and other Cohere interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.
Practice these live with InterviewChamp.AI →