LeetCode ยท #23

Merge k Sorted Lists Solution

Merge k sorted linked lists into one sorted linked list and return its head.

01
Section One ยท Problem

Problem Statement

๐Ÿท๏ธ

Difficulty

Hard

๐Ÿ”—

LeetCode

Problem #23

๐Ÿ—๏ธ

Pattern

Min-Heap or Divide & Conquer

You are given an array of k linked lists lists, each sorted in ascending order. Merge all the linked lists into one sorted linked list and return it.

Example
Input: lists = [[1,4,5],[1,3,4],[2,6]] Output: 1โ†’1โ†’2โ†’3โ†’4โ†’4โ†’5โ†’6
Constraints
โ€ข k == lists.length โ€ข 0 โ‰ค k โ‰ค 10โด โ€ข 0 โ‰ค lists[i].length โ‰ค 500 โ€ข -10โด โ‰ค lists[i][j] โ‰ค 10โด โ€ข Total nodes โ‰ค 10โด
02
Section Two ยท Approach 1

Merge One-by-One โ€” O(n ยท k)

Merge list 1 into list 2, then result into list 3, etc. Each merge is O(n), and we do k-1 merges. The merged list grows, making later merges expensive: total O(nยทk).

Problem: The merged list keeps growing, so each subsequent merge gets longer. Using a min-heap or divide-and-conquer ensures we always pick the smallest element efficiently, reducing to O(n log k).
03
Section Three ยท Approach 2

Min-Heap / Divide & Conquer โ€” O(n log k)

Min-Heap: Maintain a heap of size k holding the current head of each list. Pop the smallest, link it to the result, push the next node from that list. Each pop/push is O(log k), and we process n total nodes.

Divide & Conquer: Pair up lists and merge each pair. Repeat until one list remains โ€” like merge sort's merge phase. log k rounds, each processing n total nodes.

๐Ÿ’ก Mental model (Heap): Imagine k conveyor belts, each delivering items in order. A judge stands at the merge point with a "smallest item display" (the heap). They always pick from the belt with the smallest current item and place it on the output belt. Checking k belts with a heap takes O(log k) per pick.
Algorithm โ€” Min-Heap
  • Step 1: Add the head of each non-null list to a min-heap (ordered by node value).
  • Step 2: While heap is not empty: pop smallest node, link to result.
  • Step 3: If popped node has a next, push next to heap.
  • Step 4: Return dummy.next.
๐ŸŽฏ Divide & Conquer alternative:
  • Merge lists[0] with lists[k/2], lists[1] with lists[k/2+1], etc. Reduces k lists to k/2. Repeat log k times.
  • Same O(n log k) complexity, no heap needed โ€” useful in languages without a built-in priority queue.
04
Section Four ยท Trace

Visual Walkthrough

Trace min-heap approach with [[1,4,5],[1,3,4],[2,6]]:

Min-Heap โ€” always extract the smallest head
3 SORTED LISTS L0: 1 โ†’ 4 โ†’ 5 L1: 1 โ†’ 3 โ†’ 4 L2: 2 โ†’ 6 HEAP OPERATIONS (pop min โ†’ link โ†’ push next) Pop 1(L0) โ†’ output 1. Push 4(L0). heap: [1(L1), 2(L2), 4(L0)] Pop 1(L1) โ†’ output 1. Push 3(L1). heap: [2(L2), 3(L1), 4(L0)] Pop 2(L2) โ†’ output 2. Push 6(L2). heap: [3(L1), 4(L0), 6(L2)] Pop 3(L1) โ†’ output 3. Push 4(L1). heap: [4(L0), 4(L1), 6(L2)] Pop 4(L0) โ†’ output 4, push 5(L0). Pop 4(L1) โ†’ output 4, done L1. Pop 5(L0) โ†’ output 5, done L0. Pop 6(L2) โ†’ output 6, done L2. Heap empty! MERGED RESULT 1 โ†’ 1 โ†’ 2 โ†’ 3 โ†’ 4 โ†’ 4 โ†’ 5 โ†’ 6 Answer: 1โ†’1โ†’2โ†’3โ†’4โ†’4โ†’5โ†’6 โœ“
05
Section Five ยท Implementation

Code โ€” Java & Python

Java โ€” Min-Heap (PriorityQueue)
import java.util.PriorityQueue; class Solution { public ListNode mergeKLists(ListNode[] lists) { PriorityQueue<ListNode> heap = new PriorityQueue<>( (a, b) -> a.val - b.val); for (ListNode head : lists) if (head != null) heap.offer(head); ListNode dummy = new ListNode(0); ListNode tail = dummy; while (!heap.isEmpty()) { ListNode node = heap.poll(); tail.next = node; tail = tail.next; if (node.next != null) heap.offer(node.next); } return dummy.next; } }
Python โ€” Min-Heap (heapq)
import heapq class Solution: def mergeKLists(self, lists: list) -> Optional[ListNode]: heap = [] for i, head in enumerate(lists): if head: heapq.heappush(heap, (head.val, i, head)) dummy = tail = ListNode(0) while heap: val, i, node = heapq.heappop(heap) tail.next = node tail = tail.next if node.next: heapq.heappush(heap, (node.next.val, i, node.next)) return dummy.next
06
Section Six ยท Analysis

Complexity Analysis

ApproachTimeSpaceTrade-off
Merge one-by-oneO(n ยท k)O(1)Simple but slow for large k
Collect + sortO(n log n)O(n)Ignores sorted structure
Min-Heap โ† optimal O(n log k) O(k) Heap of size k, pop/push log k each
Divide & Conquer O(n log k) O(log k) Recursion stack for pairing
n = total nodes across all lists.:
  • The heap holds at most k nodes (one per list).
  • Each of the n nodes is pushed and popped once โ€” O(log k) per operation โ†’ O(n log k) total.
07
Section Seven ยท Edge Cases

Edge Cases & Pitfalls

CaseInputWhy It Matters
Empty array[]No lists โ†’ return null. Heap starts empty.
All empty lists[null, null]Nothing added to heap โ†’ return null.
Single list[[1,2,3]]Return it as-is โ€” no merge needed.
k=10โด, short listsMany single-node listsHeap of 10โด โ€” still fast (log 10โด โ‰ˆ 14).
โš  Common Mistake (Python): Pushing (node.val, node) to heapq. When two nodes have the same value, Python tries to compare nodes (which aren't comparable). Include a tiebreaker index: (node.val, i, node) where i is a unique counter.

โ† Back to Linked List problems