Merge k Sorted Lists
LeetCode #23 · Hard · Linked Lists · C++ solution with worked-out approach and complexity analysis.
Approach
Frequency Map (Using std::map)
Strategy
- 1Use a frequency map (ordered by key) to store counts of all values in
- 2Traverse each linked list once to populate the map.
- 3Construct a new linked list by iterating through the map in ascending
Time
O(N log M)
N is total nodes and M is absolute range or unique count.
Space
O(M)
the map.
Problem description(from LeetCode)
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.
Examples
Example 1
- Input:
- lists = [[1,4,5],[1,3,4],[2,6]]
- Output:
- [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.
C++ Solution
solution.cpp
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
struct compare {
bool operator()(ListNode *a, ListNode *b) { return a->val > b->val; }
};
ListNode *mergeKListsOptimized(vector<ListNode *> &lists) {
priority_queue<ListNode *, vector<ListNode *>, compare> pq;
for (ListNode *list : lists) {
if (list) pq.push(list);
}
ListNode *dummy = new ListNode(0);
ListNode *tail = dummy;
while (!pq.empty()) {
ListNode *top = pq.top();
pq.pop();
tail->next = top;
tail = tail->next;
if (top->next) pq.push(top->next);
}
return dummy->next;
}
};
class Solution {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
map<int, int> mp;
for (ListNode *list : lists) {
ListNode *temp = list;
while (temp != nullptr) {
mp[temp->val]++;
temp = temp->next;
}
}
if (mp.empty()) return nullptr;
ListNode *dummy = new ListNode(0);
ListNode *tail = dummy;
for (auto &[num, count] : mp) {
while (count--) {
tail->next = new ListNode(num);
tail = tail->next;
}
}
return dummy->next;
}
/*
* Approach 2: Optimized using Priority Queue (Min-Heap)
*
* Strategy:
* 1. Use a min-heap to store the head of each list.
* 2. Repeatedly extract the smallest node from the heap and add it to the
* merged list.
* 3. If the extracted node has a next node, add that next node to the heap.
*
* Time Complexity: O(N log k) where N is the total number of nodes and k is
* the number of lists.
* Space Complexity: O(k) for the priority queue.
*/
struct compare {
bool operator()(ListNode *a, ListNode *b) { return a->val > b->val; }
};
ListNode *mergeKListsOptimized(vector<ListNode *> &lists) {
priority_queue<ListNode *, vector<ListNode *>, compare> pq;
for (ListNode *list : lists) {
if (list) pq.push(list);
}
ListNode *dummy = new ListNode(0);
ListNode *tail = dummy;
while (!pq.empty()) {
ListNode *top = pq.top();
pq.pop();
tail->next = top;
tail = tail->next;
if (top->next) pq.push(top->next);
}
return dummy->next;
}
};