🧠
Optimal (Min-Heap / Priority Queue)
💡 This approach uses a min-heap to always pick the smallest current node among all lists, merging efficiently in O(N log k) time.
Intuition
Put the head of each list into a min-heap. Extract the smallest node, add it to the merged list, then add the next node from that list to the heap. Repeat until heap is empty.
Algorithm
- Initialize a min-heap and insert the head node of each non-empty list.
- Create a dummy node to build the merged list.
- While the heap is not empty, extract the smallest node and append it to the merged list.
- If the extracted node has a next node, insert it into the heap.
- Return the merged list starting from dummy.next.
💡 This approach efficiently merges by always choosing the smallest available node, avoiding repeated full merges.
from typing import List, Optional
import heapq
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def __lt__(self, other):
return self.val < other.val
def mergeKLists(lists: List[Optional[ListNode]]) -> Optional[ListNode]:
heap = []
for node in lists:
if node:
heapq.heappush(heap, node)
dummy = ListNode(0)
current = dummy
while heap:
smallest = heapq.heappop(heap)
current.next = smallest
current = current.next
if smallest.next:
heapq.heappush(heap, smallest.next)
return dummy.next
# Driver code
if __name__ == '__main__':
def create_list(arr):
dummy = ListNode(0)
cur = dummy
for v in arr:
cur.next = ListNode(v)
cur = cur.next
return dummy.next
lists = [create_list([1,4,5]), create_list([1,3,4]), create_list([2,6])]
merged = mergeKLists(lists)
while merged:
print(merged.val, end='->' if merged.next else '')
merged = merged.next
Line Notes
import heapqUse Python's built-in min-heap implementation
def __lt__(self, other)Define less-than for ListNode to allow heap comparisons
for node in lists:Add all non-empty list heads to heap
heapq.heappush(heap, node)Push node into min-heap
smallest = heapq.heappop(heap)Pop smallest node from heap
current.next = smallestAppend smallest node to merged list
if smallest.next:If next node exists, push it to heap
return dummy.nextReturn head of merged sorted list
import java.util.*;
class ListNode {
int val;
ListNode next;
ListNode(int val) { this.val = val; }
}
public class Solution {
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<ListNode> heap = new PriorityQueue<>(Comparator.comparingInt(a -> a.val));
for (ListNode node : lists) {
if (node != null) heap.offer(node);
}
ListNode dummy = new ListNode(0);
ListNode current = dummy;
while (!heap.isEmpty()) {
ListNode smallest = heap.poll();
current.next = smallest;
current = current.next;
if (smallest.next != null) heap.offer(smallest.next);
}
return dummy.next;
}
// Driver code
public static void main(String[] args) {
Solution sol = new Solution();
ListNode l1 = createList(new int[]{1,4,5});
ListNode l2 = createList(new int[]{1,3,4});
ListNode l3 = createList(new int[]{2,6});
ListNode[] lists = new ListNode[]{l1, l2, l3};
ListNode merged = sol.mergeKLists(lists);
printList(merged);
}
static ListNode createList(int[] arr) {
ListNode dummy = new ListNode(0);
ListNode cur = dummy;
for (int v : arr) {
cur.next = new ListNode(v);
cur = cur.next;
}
return dummy.next;
}
static void printList(ListNode head) {
while (head != null) {
System.out.print(head.val);
if (head.next != null) System.out.print("->");
head = head.next;
}
System.out.println();
}
}
Line Notes
PriorityQueue<ListNode> heap = new PriorityQueue<>(Comparator.comparingInt(a -> a.val));Min-heap ordered by node value
for (ListNode node : lists)Add all non-null list heads to heap
ListNode smallest = heap.poll();Extract smallest node from heap
current.next = smallest;Append smallest node to merged list
if (smallest.next != null) heap.offer(smallest.next);Add next node from extracted node's list
return dummy.next;Return merged list head
public static void main(String[] args)Driver code to test solution
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};
struct Compare {
bool operator()(ListNode* a, ListNode* b) {
return a->val > b->val;
}
};
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
priority_queue<ListNode*, vector<ListNode*>, Compare> heap;
for (auto node : lists) {
if (node) heap.push(node);
}
ListNode dummy(0);
ListNode* current = &dummy;
while (!heap.empty()) {
ListNode* smallest = heap.top();
heap.pop();
current->next = smallest;
current = current->next;
if (smallest->next) heap.push(smallest->next);
}
return dummy.next;
}
};
// Helper functions
ListNode* createList(const vector<int>& arr) {
ListNode dummy(0);
ListNode* cur = &dummy;
for (int v : arr) {
cur->next = new ListNode(v);
cur = cur->next;
}
return dummy.next;
}
void printList(ListNode* head) {
while (head) {
cout << head->val;
if (head->next) cout << "->";
head = head->next;
}
cout << endl;
}
int main() {
Solution sol;
vector<ListNode*> lists = {createList({1,4,5}), createList({1,3,4}), createList({2,6})};
ListNode* merged = sol.mergeKLists(lists);
printList(merged);
return 0;
}
Line Notes
priority_queue<ListNode*, vector<ListNode*>, Compare> heap;Min-heap with custom comparator for ListNode
for (auto node : lists)Add all non-null list heads to heap
ListNode* smallest = heap.top();Get smallest node from heap
current->next = smallest;Append smallest node to merged list
if (smallest->next) heap.push(smallest->next);Add next node from extracted node's list
return dummy.next;Return merged list head
int main()Driver code to test solution
class ListNode {
constructor(val = 0, next = null) {
this.val = val;
this.next = next;
}
}
class MinHeap {
constructor() {
this.heap = [];
}
swap(i, j) {
[this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]];
}
parent(i) { return Math.floor((i - 1) / 2); }
left(i) { return 2 * i + 1; }
right(i) { return 2 * i + 2; }
push(node) {
this.heap.push(node);
this.bubbleUp(this.heap.length - 1);
}
bubbleUp(i) {
while (i > 0 && this.heap[this.parent(i)].val > this.heap[i].val) {
this.swap(i, this.parent(i));
i = this.parent(i);
}
}
pop() {
if (this.heap.length === 0) return null;
const top = this.heap[0];
const end = this.heap.pop();
if (this.heap.length > 0) {
this.heap[0] = end;
this.bubbleDown(0);
}
return top;
}
bubbleDown(i) {
let smallest = i;
const left = this.left(i);
const right = this.right(i);
if (left < this.heap.length && this.heap[left].val < this.heap[smallest].val) smallest = left;
if (right < this.heap.length && this.heap[right].val < this.heap[smallest].val) smallest = right;
if (smallest !== i) {
this.swap(i, smallest);
this.bubbleDown(smallest);
}
}
isEmpty() {
return this.heap.length === 0;
}
}
function mergeKLists(lists) {
const heap = new MinHeap();
for (const node of lists) {
if (node) heap.push(node);
}
const dummy = new ListNode(0);
let current = dummy;
while (!heap.isEmpty()) {
const smallest = heap.pop();
current.next = smallest;
current = current.next;
if (smallest.next) heap.push(smallest.next);
}
return dummy.next;
}
// Driver code
function createList(arr) {
const dummy = new ListNode(0);
let cur = dummy;
for (const v of arr) {
cur.next = new ListNode(v);
cur = cur.next;
}
return dummy.next;
}
const lists = [createList([1,4,5]), createList([1,3,4]), createList([2,6])];
const merged = mergeKLists(lists);
let cur = merged;
let output = '';
while (cur) {
output += cur.val + (cur.next ? '->' : '');
cur = cur.next;
}
console.log(output);
Line Notes
class MinHeapCustom min-heap implementation for ListNode objects
this.heap.push(node);Add node to heap array
bubbleUpRestore heap property after insertion
pop()Remove and return smallest node from heap
bubbleDownRestore heap property after removal
for (const node of lists)Add all non-null list heads to heap
current.next = smallest;Append smallest node to merged list
if (smallest.next) heap.push(smallest.next);Add next node from extracted node's list
TimeO(N log k) where N is total nodes and k is number of lists
SpaceO(k) for the heap
Each node is pushed and popped from the heap once; heap size is at most k, so operations cost O(log k).
💡 For example, with 1000 total nodes and 10 lists, this is about 1000 * log2(10) ≈ 3000 operations, very efficient.
Interview Verdict: Accepted and optimal for large k and large total nodes
This is the best approach to implement in interviews for this problem.