← Home

23. Merge k Sorted Lists

LeetCode article · C++ solution
Website made by wuisabel-gif · Original C++ code by keineahnung2345
heap / priority queueC++Markdown
23

A good way into this one is to ask: what do we need to remember so we never redo work blindly? For 23. Merge k Sorted Lists, the solution in this repository is mainly a heap / priority queue solution.

Guide

What?

The first job is to translate the English prompt into state, transition, and stopping conditions. Instead of trying to be clever immediately, read the code as a sequence of questions:

  • What state are we keeping?
  • How do we move from one state to the next?
  • When do we know the answer is already determined?

For this file, the main tools are: heap / priority queue.

The notes already sitting in the source point us in the right direction:

  • Approach 1: Brute Force
  • time: O(NlogN), collect all nodes and then sort
  • space: O(N)
  • Approach 2: Compare the head of linked list

Guide

When?

This pattern shows up when the brute force version has too many repeated checks, too many possible branches, or too much bookkeeping to do by hand. The accepted code reduces that pressure by storing exactly the information that remains useful later.

The important function names to track are mergeKLists, mergeTwoLists.

Guide

Why?

The win comes from making each line carry responsibility: store the useful state, discard the rest, keep moving.

  • The queue gives the solution a level-by-level or frontier-style traversal.
  • The heap keeps the best candidate available without sorting the whole world every time.
  • The final return is not magic; it is the invariant after the loops or recursion have finished doing their accounting.

Guide

How?

Walk through the solution in this order:

  1. Read the setup variables first.
  2. Follow the main loop or recursive helper next.
  3. Watch where invalid states get skipped.
  4. Check which value survives to the return statement.

The most important competitive-programming habit here is to trust the invariant. Once the invariant is right, the loops become much less scary.

Guide

Complexity

  • Time: O(KN), space: O(1)
  • Space: O(K) for the priority queue

Guide

C++ Solution

Your submission

The accepted solution

solution.cpp
01//Approach 1: Brute Force
02//time: O(NlogN), collect all nodes and then sort
03//space: O(N)
04
05//Approach 2: Compare the head of linked list
06//time: O(KN), k: #linked list
07//space: O(N) -> O(1) if in-place
08
09//Approach 3: Optimize Approach 2 by Priority Queue
10//Runtime: 36 ms, faster than 64.12% of C++ online submissions for Merge k Sorted Lists.
11//Memory Usage: 12 MB, less than 71.35% of C++ online submissions for Merge k Sorted Lists.
12//time: O(NlogK)
13//space: O(K) for the priority queue
14/**
15 * Definition for singly-linked list.
16 * struct ListNode {
17 *     int val;
18 *     ListNode *next;
19 *     ListNode() : val(0), next(nullptr) {}
20 *     ListNode(int x) : val(x), next(nullptr) {}
21 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
22 * };
23 */
24class Solution {
25public:
26    ListNode* mergeKLists(vector<ListNode*>& lists) {
27        auto comp = [](const ListNode* a, const ListNode* b){
28            return a->val > b->val;
29        };
30        priority_queue<ListNode*, vector<ListNode*>, decltype(comp)> pq(comp);
31        
32        for(ListNode* node : lists){
33            if(node != nullptr) pq.push(node);
34        }
35        
36        ListNode* head = new ListNode(-1);
37        ListNode* cur = head;
38        
39        while(!pq.empty()){
40            ListNode* node = pq.top(); pq.pop();
41            cur->next = node;
42            node = node->next;
43            cur = cur->next;
44            cur->next = nullptr;
45            if(node != nullptr){
46                pq.push(node);
47            }
48        }
49        
50        return head->next;
51    }
52};
53
54//Approach 4: Merge lists one by one
55//extended from "21. Merge Two Sorted Lists"
56//Runtime: 248 ms, faster than 14.96% of C++ online submissions for Merge k Sorted Lists.
57//Memory Usage: 12.1 MB, less than 61.91% of C++ online submissions for Merge k Sorted Lists.
58//time: O(KN), space: O(1)
59class Solution {
60public:
61    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
62        if(l1 == nullptr) return l2;
63        if(l2 == nullptr) return l1;
64        if(l1->val <= l2->val){
65            l1->next = mergeTwoLists(l1->next, l2);
66            return l1;
67        }else{
68            l2->next = mergeTwoLists(l2->next, l1);
69            return l2;
70        }
71    };
72    
73    ListNode* mergeKLists(vector<ListNode*>& lists) {
74        ListNode *head = nullptr;
75        
76        for(int i = 0; i < lists.size(); ++i){
77            head = mergeTwoLists(head, lists[i]);
78        }
79        
80        return head;
81    }
82};
83
84//Approach 5: Merge with Divide And Conquer
85//Runtime: 16 ms, faster than 99.77% of C++ online submissions for Merge k Sorted Lists.
86//Memory Usage: 12.3 MB, less than 40.56% of C++ online submissions for Merge k Sorted Lists.
87//time: O(NlogK), space: O(1)
88class Solution {
89public:
90    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
91        if(l1 == nullptr) return l2;
92        if(l2 == nullptr) return l1;
93        if(l1->val <= l2->val){
94            l1->next = mergeTwoLists(l1->next, l2);
95            return l1;
96        }else{
97            l2->next = mergeTwoLists(l2->next, l1);
98            return l2;
99        }
100    };
101    
102    ListNode* mergeKLists(vector<ListNode*>& lists) {
103        int n = lists.size();
104        if(n == 0) return nullptr;
105        
106        int interval = 1;
107        
108        /*
109        divide and conquer
110        l0, l1, l2, l3, l4, l5
111        -> new l0(l0+l1), new l2(l2+l3), new l4(l4+l5)
112        -> new l0(l0+l2), l4
113        -> new l0(l0+l4)
114        */
115        while(interval < n){
116            for(int i = 0; i+interval < n; i += interval*2){
117                lists[i] = mergeTwoLists(lists[i], lists[i+interval]);
118            }
119            interval *= 2;
120        }
121        
122        return lists[0];
123    }
124};

Cost

Complexity

Time
O(KN), space: O(1)
Dominated by the main traversal, recursion, or data-structure operations in the code.
Space
O(K) for the priority queue
Auxiliary state plus the answer structure where the problem requires one.