Merge k Sorted Lists hard
Problem Statement
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
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, 2->6 ] merging them into one sorted list: 1->1->2->3->4->4->5->6
Example 2
Input: lists = [] Output: []
Steps
-
Handle Empty Input: If the input list
lists
is empty or contains only null lists, return an empty list. -
PriorityQueue for Comparison: Utilize a
PriorityQueue
(min-heap) to efficiently manage the heads of all k sorted lists. ThePriorityQueue
will automatically order the nodes based on their values. We'll use a custom comparator to compareListNode
objects based on theirval
property. -
Iterative Merging:
- Initialize a dummy head node to simplify the construction of the merged list.
- Add the head nodes of all non-null input lists to the
PriorityQueue
. - While the
PriorityQueue
is not empty:- Remove the node with the smallest value (the head of the smallest sublist).
- Add this node to the merged list.
- If the removed node has a next node, add that next node to the
PriorityQueue
.
-
Return Merged List: Return the next node after the dummy head, which is the head of the merged sorted list.
Explanation
The key to efficiently solving this problem is using a priority queue (min-heap). A min-heap keeps track of the smallest element at the top. By placing the heads of all k sorted lists into the min-heap, we can repeatedly extract the smallest element, add it to our result, and replace it with the next element from its original list. This ensures that the merged list is always built in sorted order. The time complexity is dominated by the number of nodes processed, while the space complexity comes from the priority queue.
Code
import java.util.PriorityQueue;
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) {
return null;
}
PriorityQueue<ListNode> pq = new PriorityQueue<>( (a, b) -> a.val - b.val); // Min-heap comparator
// Add the head nodes of all lists to the priority queue
for (ListNode node : lists) {
if (node != null) {
pq.offer(node);
}
}
ListNode dummy = new ListNode(0); // Dummy node to simplify construction
ListNode tail = dummy;
while (!pq.isEmpty()) {
ListNode curr = pq.poll(); // Get the smallest node
tail.next = curr; // Add it to the merged list
tail = tail.next; // Move the tail pointer
if (curr.next != null) {
pq.offer(curr.next); // Add the next node from the same list
}
}
return dummy.next; // Return the head of the merged list
}
}
Complexity
-
Time Complexity: O(N log k), where N is the total number of nodes in all k lists and k is the number of linked lists. Each node is added to and removed from the priority queue once, which takes O(log k) time.
-
Space Complexity: O(k), where k is the number of linked lists. The space complexity is dominated by the priority queue, which holds at most k nodes at any time (one for each list).