Merge K Sorted Lists

hard

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 the head of the merged list.

Examples

Example 1

  • Input: lists = [[1,4,5],[1,3,4],[2,6]]
  • Output: [1,1,2,3,4,4,5,6]
  • Explanation: The linked lists are 1->4->5, 1->3->4, and 2->6. Merging them into one sorted list gives 1->1->2->3->4->4->5->6.

Example 2

  • Input: lists = []
  • Output: []

Example 3

  • Input: lists = [[]]
  • Output: []

Constraints

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] is sorted in ascending order.
  • The sum of lists[i].length will not exceed 10^4.

Approaches

2 worked solutions ranked from straightforward to optimal — each with intuition, algorithm, complexity, and verified code in C++, Java, Python, and Go.

Approach 1

Min-Heap (Priority Queue)

Intuition

The idea is to use a min-heap (priority queue) to always pick the smallest element among the heads of all k lists. We start by pushing the head of every non-empty list into the heap. Then we repeatedly extract the minimum node, append it to our result list, and push that node's next pointer into the heap if it is not null. This ensures we always process nodes in sorted order. The heap size is at most k, making each insert and extract operation O(log k).

Algorithm

  1. Create a min-heap and push the head node of each non-empty list.
  2. Create a dummy node to build the result list. Initialize a `current` pointer to the dummy.
  3. While the heap is not empty, extract the node with the smallest value.
  4. Attach the extracted node to `current.next` and advance `current`.
  5. If the extracted node has a next node, push it into the heap.
  6. Return `dummy.next` as the head of the merged list.
TimeO(n log k)SpaceO(k)

Code (C++ · Java · Python · Go)

C++17
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        auto cmp = [](ListNode* a, ListNode* b) {
            return a->val > b->val;
        };
        priority_queue<ListNode*, vector<ListNode*>, decltype(cmp)> pq(cmp);

        for (auto* list : lists) {
            if (list != nullptr) {
                pq.push(list);
            }
        }

        ListNode dummy(0);
        ListNode* current = &dummy;

        while (!pq.empty()) {
            ListNode* node = pq.top();
            pq.pop();
            current->next = node;
            current = current->next;
            if (node->next != nullptr) {
                pq.push(node->next);
            }
        }

        return dummy.next;
    }
};
Java 21
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        PriorityQueue<ListNode> pq = new PriorityQueue<>((a, b) -> a.val - b.val);

        for (ListNode list : lists) {
            if (list != null) {
                pq.offer(list);
            }
        }

        ListNode dummy = new ListNode(0);
        ListNode current = dummy;

        while (!pq.isEmpty()) {
            ListNode node = pq.poll();
            current.next = node;
            current = current.next;
            if (node.next != null) {
                pq.offer(node.next);
            }
        }

        return dummy.next;
    }
}
Python 3.12
import heapq

def mergeKLists(lists: List[Optional[ListNode]]) -> Optional[ListNode]:
    heap = []
    for i, node in enumerate(lists):
        if node:
            heapq.heappush(heap, (node.val, i, node))

    dummy = ListNode(0)
    current = dummy

    while heap:
        val, idx, node = heapq.heappop(heap)
        current.next = node
        current = current.next
        if node.next:
            heapq.heappush(heap, (node.next.val, idx, node.next))

    return dummy.next
Go 1.26
import "container/heap"

type MinHeap []*ListNode

func (h MinHeap) Len() int            { return len(h) }
func (h MinHeap) Less(i, j int) bool   { return h[i].Val < h[j].Val }
func (h MinHeap) Swap(i, j int)        { h[i], h[j] = h[j], h[i] }
func (h *MinHeap) Push(x interface{})  { *h = append(*h, x.(*ListNode)) }
func (h *MinHeap) Pop() interface{} {
    old := *h
    n := len(old)
    node := old[n-1]
    *h = old[:n-1]
    return node
}

func mergeKLists(lists []*ListNode) *ListNode {
    h := &MinHeap{}
    heap.Init(h)

    for _, list := range lists {
        if list != nil {
            heap.Push(h, list)
        }
    }

    dummy := &ListNode{}
    current := dummy

    for h.Len() > 0 {
        node := heap.Pop(h).(*ListNode)
        current.Next = node
        current = current.Next
        if node.Next != nil {
            heap.Push(h, node.Next)
        }
    }

    return dummy.Next
}
Approach 2

Divide and Conquer

Intuition

This approach mirrors merge sort. Instead of merging all k lists at once, we repeatedly merge pairs of lists until only one remains. In each round, we pair up adjacent lists and merge each pair, halving the number of lists. This continues until we are left with a single merged list. Each merge of two sorted lists takes O(n) time, and we perform O(log k) rounds, resulting in O(n log k) total time. This avoids the overhead of maintaining a heap.

Algorithm

  1. If the input list is empty, return null.
  2. While there is more than one list, merge lists in pairs.
  3. For each pair of adjacent lists at indices `i` and `i+1`, merge them into a single sorted list using a standard two-list merge.
  4. Replace the original lists with the merged results, halving the count.
  5. Repeat until one list remains. Return it.
TimeO(n log k)SpaceO(1)

Code (C++ · Java · Python · Go)

C++17
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode dummy(0);
        ListNode* 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 ? l1 : l2;
        return dummy.next;
    }

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if (lists.empty()) return nullptr;

        int size = lists.size();
        while (size > 1) {
            for (int i = 0; i < size / 2; i++) {
                lists[i] = mergeTwoLists(lists[i], lists[size - 1 - i]);
            }
            size = (size + 1) / 2;
        }

        return lists[0];
    }
};
Java 21
class Solution {
    private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(0);
        ListNode curr = dummy;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                curr.next = l1;
                l1 = l1.next;
            } else {
                curr.next = l2;
                l2 = l2.next;
            }
            curr = curr.next;
        }
        curr.next = (l1 != null) ? l1 : l2;
        return dummy.next;
    }

    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) return null;

        int size = lists.length;
        while (size > 1) {
            for (int i = 0; i < size / 2; i++) {
                lists[i] = mergeTwoLists(lists[i], lists[size - 1 - i]);
            }
            size = (size + 1) / 2;
        }

        return lists[0];
    }
}
Python 3.12
def mergeKLists(lists: List[Optional[ListNode]]) -> Optional[ListNode]:
    def merge_two(l1, l2):
        dummy = ListNode(0)
        curr = dummy
        while l1 and 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 if l1 else l2
        return dummy.next

    if not lists:
        return None

    while len(lists) > 1:
        merged = []
        for i in range(0, len(lists), 2):
            l1 = lists[i]
            l2 = lists[i + 1] if i + 1 < len(lists) else None
            merged.append(merge_two(l1, l2))
        lists = merged

    return lists[0]
Go 1.26
func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {
    dummy := &ListNode{}
    curr := dummy
    for l1 != nil && l2 != nil {
        if l1.Val <= l2.Val {
            curr.Next = l1
            l1 = l1.Next
        } else {
            curr.Next = l2
            l2 = l2.Next
        }
        curr = curr.Next
    }
    if l1 != nil {
        curr.Next = l1
    } else {
        curr.Next = l2
    }
    return dummy.Next
}

func mergeKLists(lists []*ListNode) *ListNode {
    if len(lists) == 0 {
        return nil
    }

    for len(lists) > 1 {
        var merged []*ListNode
        for i := 0; i < len(lists); i += 2 {
            if i+1 < len(lists) {
                merged = append(merged, mergeTwoLists(lists[i], lists[i+1]))
            } else {
                merged = append(merged, lists[i])
            }
        }
        lists = merged
    }

    return lists[0]
}

Frequently asked questions

What is the optimal time complexity of Merge K Sorted Lists?

The most efficient approach in this guide ("Divide and Conquer") runs in O(n log k) time and uses O(1) extra space. Earlier brute-force approaches are slower; see all 2 approaches above for the full progression.

What pattern does Merge K Sorted Lists use?

Merge K Sorted Lists is a hard-level Heap / Priority Queue problem involving Linked List, Heap / Priority Queue, Divide and Conquer. Recognizing the pattern is half the work — see the intuition section above for how to spot it.

Can Merge K Sorted Lists be solved without extra space?

The most space-efficient approach in this guide uses O(1) extra space (excluding the input). If you're aiming for in-place — see the trade-off in the algorithm section above — the brute-force approach uses O(k), while the optimized version uses O(1).

Which programming languages does this Ratta solution support?

Every approach above ships with verified, runnable solutions in C++, Java, Python, and Go. The starter templates in the workspace match the same four languages so you can practice in your interview language of choice.

Original problem: leetcode.com

Loading editor...
lists=
[[1,4,5],[1,3,4],[2,6]]
[1,1,2,3,4,4,5,6]