Given k singly linked lists where each linked list is sorted in ascending order, merge all of them into a single sorted linked list.
Example:
Input: [ [1 -> 3 -> 5], [3 -> 4], [7] ].
Output: 1 -> 3 -> 3 -> 4 -> 5 -> 7
Notes:
Constraints:
We provided three solutions.
Throughout this editorial, we will refer to the input array of the linked lists as lists, its size as k and the total number of nodes in all the given linked lists as n.
We will start with a brute force approach and will then discuss the areas of optimization to reach the optimal solutions.
All the individual lists are sorted. Therefore, a brute force is to compare the heads of all the linked lists one by one and choose the linked list whose head has the minimum value.
The head of the selected linked list will then be appended at the end of the final linked list which will initially be empty.
After this, we will advance the head node of the selected list to point to its next node.
The same procedure is continued till no node is left to be added in the final linked list, ie, when the heads of all the given lists point to a NULL node.
Time Complexity:
O(k * n).
In total, there are n nodes to be added in the final list and it will take an entire traversal of k heads on average to find the node with the minimum value.
Auxiliary Space Used:
O(1).
We used only a constant amount of extra space.
Space Complexity:
O(n + k).
Space used for input: O(n + k).
Auxiliary space used: O(1).
Space used for output: O(n).
So, the total space complexity: O(n + k).
In the above solution, we did a linear traversal of k heads to find the node with the minimum value.
This traversal can be avoided and the same work can be done more time efficiently using some extra amount of space.
We can store the heads of all the k lists in a min-heap that arranges the nodes on the basis of their values. Now, using this min-heap, we can find the head with the minimum value in O(log(k)) time.
After adding all the non-NULL heads in the min-heap, we will have to follow the following steps:
We will perform the above steps till the min-heap is non-empty.
Time Complexity:
O(n * log(k)).
We have n nodes to add and searching for the node with the minimum value in the min-heap will take an O(log(k)) time on an average.
So, since we also have n nodes to pop-out from the min-heap, it would take O(n * log(k)) time in total.
Auxiliary Space Used:
O(k).
At any point in time, the min-heap will have a maximum size of k.
Space Complexity:
Space used for input: O(n + k).
Auxiliary space used: O(k).
Space used for output: O(n).
So, total space complexity: O(n + k).
Note that we can merge k sorted linked lists by merging two lists, k - 1 number of times. We can initially merge the first two linked lists, the resultant list can be merged with the third linked list and so on. Doing this, we will finally have a single sorted linked list having all the nodes of the input lists. This process will take O(n * k) amount of time as each process of merging two lists will take O(n) time in the worst case.
As we keep merging more and more linked lists into the final list, the size of the final list keeps increasing. Therefore, we will be traversing each node in the linked list more than once.
The nodes of the first two linked lists will be involved in all the merge processes, hence they will be traversed a maximum of k - 1 number of times. Similarly, the nodes of the third list will be involved in k - 2 merge processes, hence they will be traversed a maximum of k - 2 number of times and so on.
We can reduce the number of times each node is traversed if we can somehow reduce the sizes of the subsequent lists which help us reach the final list. The size of the intermediate lists can be reduced using a divide and conquer process:
We will continue this till we are finally left with a single linked list.
The complete process will look like the following if we initially have k = 4 number of linked lists.
Note that,
In each level, we merge the first list from the beginning with the first list from the end, second list from the beginning with the second list from the end and so on. Any other way of pairing would also produce the same output.
Also note that, we traverse all of the n nodes at most once in each level and the number of such levels is equal to log(k). Therefore, each node is now traversed a maximum of log(k) number of times.
Time Complexity:
O(n * log(k)).
During each level of pairing and merging, we are traversing each of the n nodes at most once and the number of such levels is equal to log(k).
Auxiliary space used:
O(1).
We used only a constant amount of extra space.
Space Complexity:
Space used for input: O(n + k).
Auxiliary space used: O(1).
Space used for output: O(n).
So, total space complexity: O(n + k).
Given k singly linked lists where each linked list is sorted in ascending order, merge all of them into a single sorted linked list.
Example:
Input: [ [1 -> 3 -> 5], [3 -> 4], [7] ].
Output: 1 -> 3 -> 3 -> 4 -> 5 -> 7
Notes:
Constraints:
We provided three solutions.
Throughout this editorial, we will refer to the input array of the linked lists as lists, its size as k and the total number of nodes in all the given linked lists as n.
We will start with a brute force approach and will then discuss the areas of optimization to reach the optimal solutions.
All the individual lists are sorted. Therefore, a brute force is to compare the heads of all the linked lists one by one and choose the linked list whose head has the minimum value.
The head of the selected linked list will then be appended at the end of the final linked list which will initially be empty.
After this, we will advance the head node of the selected list to point to its next node.
The same procedure is continued till no node is left to be added in the final linked list, ie, when the heads of all the given lists point to a NULL node.
Time Complexity:
O(k * n).
In total, there are n nodes to be added in the final list and it will take an entire traversal of k heads on average to find the node with the minimum value.
Auxiliary Space Used:
O(1).
We used only a constant amount of extra space.
Space Complexity:
O(n + k).
Space used for input: O(n + k).
Auxiliary space used: O(1).
Space used for output: O(n).
So, the total space complexity: O(n + k).
In the above solution, we did a linear traversal of k heads to find the node with the minimum value.
This traversal can be avoided and the same work can be done more time efficiently using some extra amount of space.
We can store the heads of all the k lists in a min-heap that arranges the nodes on the basis of their values. Now, using this min-heap, we can find the head with the minimum value in O(log(k)) time.
After adding all the non-NULL heads in the min-heap, we will have to follow the following steps:
We will perform the above steps till the min-heap is non-empty.
Time Complexity:
O(n * log(k)).
We have n nodes to add and searching for the node with the minimum value in the min-heap will take an O(log(k)) time on an average.
So, since we also have n nodes to pop-out from the min-heap, it would take O(n * log(k)) time in total.
Auxiliary Space Used:
O(k).
At any point in time, the min-heap will have a maximum size of k.
Space Complexity:
Space used for input: O(n + k).
Auxiliary space used: O(k).
Space used for output: O(n).
So, total space complexity: O(n + k).
Note that we can merge k sorted linked lists by merging two lists, k - 1 number of times. We can initially merge the first two linked lists, the resultant list can be merged with the third linked list and so on. Doing this, we will finally have a single sorted linked list having all the nodes of the input lists. This process will take O(n * k) amount of time as each process of merging two lists will take O(n) time in the worst case.
As we keep merging more and more linked lists into the final list, the size of the final list keeps increasing. Therefore, we will be traversing each node in the linked list more than once.
The nodes of the first two linked lists will be involved in all the merge processes, hence they will be traversed a maximum of k - 1 number of times. Similarly, the nodes of the third list will be involved in k - 2 merge processes, hence they will be traversed a maximum of k - 2 number of times and so on.
We can reduce the number of times each node is traversed if we can somehow reduce the sizes of the subsequent lists which help us reach the final list. The size of the intermediate lists can be reduced using a divide and conquer process:
We will continue this till we are finally left with a single linked list.
The complete process will look like the following if we initially have k = 4 number of linked lists.
Note that,
In each level, we merge the first list from the beginning with the first list from the end, second list from the beginning with the second list from the end and so on. Any other way of pairing would also produce the same output.
Also note that, we traverse all of the n nodes at most once in each level and the number of such levels is equal to log(k). Therefore, each node is now traversed a maximum of log(k) number of times.
Time Complexity:
O(n * log(k)).
During each level of pairing and merging, we are traversing each of the n nodes at most once and the number of such levels is equal to log(k).
Auxiliary space used:
O(1).
We used only a constant amount of extra space.
Space Complexity:
Space used for input: O(n + k).
Auxiliary space used: O(1).
Space used for output: O(n).
So, total space complexity: O(n + k).
Attend our free webinar to amp up your career and get the salary you deserve.