Introduction
This pattern helps us solve problems that involve a list of sorted arrays.
Whenever we are given ‘K’ sorted arrays, we can use a Heap to efficiently perform a sorted traversal of all the elements of all arrays. We can push the smallest (first) element of each sorted array in a Min Heap to get the overall minimum. While inserting elements to the Min Heap we keep track of which array the element came from. We can, then, remove the top element from the heap to get the smallest element and push the next element from the same array, to which this smallest element belonged, to the heap. We can repeat this process to make a sorted traversal of all elements.
Let’s see this pattern in action.
Merge K Sorted Lists (medium)
Top Interview 150 | 23. Merge k Sorted Lists Design Gurus Educative.io
Problem Statemen
Given an array of ‘K’ sorted LinkedLists, merge them into one sorted list.
Example 1:
1 | Input: L1=[2, 6, 8], L2=[3, 6, 7], L3=[1, 3, 4] |
Example 2:
1 | Input: L1=[5, 8, 9], L2=[1, 7] |
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.
Solution
A brute force solution could be to add all elements of the given ‘K’ lists to one list and sort it. If there are a total of ‘N’ elements in all the input lists, then the brute force solution will have a time complexity of O(NlogN) as we will need to sort the merged list. Can we do better than this? How can we utilize the fact that the input lists are individually sorted?
If we have to find the smallest element of all the input lists, we have to compare only the smallest (i.e. the first) element of all the lists. Once we have the smallest element, we can put it in the merged list. Following a similar pattern, we can then find the next smallest element of all the lists to add it to the merged list.
The best data structure that comes to mind to find the smallest number among a set of ‘K’ numbers is a Heap). Let’s see how can we use a heap to find a better algorithm.
- We can insert the first element of each array in a Min Heap.
- After this, we can take out the smallest (top) element from the heap and add it to the merged list.
- After removing the smallest element from the heap, we can insert the next element of the same list into the heap.
- We can repeat steps 2 and 3 to populate the merged list in sorted order.
Let’s take the Example-1 mentioned above to go through each step of our algorithm:
Given lists: L1=[2, 6, 8]
, L2=[3, 6, 7]
,L3=[1, 3, 4]
After inserting the 1st element of each list, the heap will have the following elements:
We’ll take the top number from the heap, insert it into the merged list and add the next number in the heap.
Again, we’ll take the top element of the heap, insert it into the merged list and add the next number to the heap.
Repeating the above step, take the top element of the heap, insert it into the merged list and add the next number to the heap. As there are two 3s in the heap, we can pick anyone but we need to take the next element from the corresponding list to insert in the heap.
We’ll repeat the above step to populate our merged array.
Code
Here is what our algorithm will look like:
1 | from heapq import * |
Time complexity
Since we’ll be going through all the elements of all arrays and will be removing/adding one element to the heap in each step, the time complexity of the above algorithm will be O(N\logK)*, where ‘N’ is the total number of elements in all the ‘K’ input arrays.
Space complexity
The space complexity will be O(K) because, at any time, our min-heap will be storing one number from all the ‘K’ input arrays.
Kth Smallest Number in M Sorted Lists (Medium)
Similar | 378. Kth Smallest Element in a Sorted Matrix Design Gurus Educative.io
Problem Statement
Given ‘M’ sorted arrays, find the K’th smallest number among all the arrays.
Example 1:
1 | Input: L1=[2, 6, 8], L2=[3, 6, 7], L3=[1, 3, 4], K=5 |
Example 2:
1 | Input: L1=[5, 8, 9], L2=[1, 7], K=3 |
Solution
This problem follows the K-way merge pattern and we can follow a similar approach as discussed in Merge K Sorted Lists.
We can start merging all the arrays, but instead of inserting numbers into a merged list, we will keep count to see how many elements have been inserted in the merged list. Once that count is equal to ‘K’, we have found our required number.
A big difference from Merge K Sorted Lists is that in this problem, the input is a list of arrays compared to LinkedLists. This means that when we want to push the next number in the heap we need to know what the index of the current number in the current array was. To handle this, we will need to keep track of the array and the element indices.
Code
Here is what our algorithm will look like:
1 | from heapq import * |
Time complexity
Since we’ll be going through at most ‘K’ elements among all the arrays, and we will remove/add one element in the heap in each step, the time complexity of the above algorithm will be O(KlogM)O(K∗log*M) where ‘M’ is the total number of input arrays.
Space complexity
The space complexity will be O(M)O(M) because, at any time, our min-heap will be storing one number from all the ‘M’ input arrays.
Similar Problems
Similar | Top Interview 150 | 4. Median of Two Sorted Arrays Design Gurus Educative.io
Problem 1: Given ‘M’ sorted arrays, find the median number among all arrays.
Solution: This problem is similar to our parent problem with K=Median. So if there are ‘N’ total numbers in all the arrays we need to find the K’th minimum number where K=N/2.
Problem 2: Given a list of ‘K’ sorted arrays, merge them into one sorted list.
Solution: This problem is similar to Merge K Sorted Lists except that the input is a list of arrays compared to LinkedLists. To handle this, we can use a similar approach as discussed in our parent problem by keeping a track of the array and the element indices.
#Kth Smallest Number in a Sorted Matrix (Hard)
378. Kth Smallest Element in a Sorted Matrix Design Gurus Educative.io
Problem Statement
Given an N\ N matrix where *each row and column is sorted in ascending order, find the Kth smallest element in the matrix.
Example 1:
1 | Input: Matrix=[ |
Constraints:
n == matrix.length == matrix[i].length
1 <= n <= 300
- -10^9 <= matrix[i][j] <= 10^9
- All the rows and columns of matrix are guaranteed to be sorted in non-decreasing order.
- 1 <= k <= n^2
Solution
This problem follows the K-way merge pattern and can be easily converted to Kth Smallest Number in M Sorted Lists. As each row (or column) of the given matrix can be seen as a sorted list, we essentially need to find the Kth smallest number in ‘N’ sorted lists.
Code
Here is what our algorithm will look like:
1 | # 和上面一道题一摸一样 |
Time complexity
First, we inserted at most ‘K’ or one element from each of the ‘N’ rows, which will take O(min(K, N)). Then we went through at most ‘K’ elements in the matrix and remove/add one element in the heap in each step. As we can’t have more than ‘N’ elements in the heap in any condition, therefore, the overall time complexity of the above algorithm will be O(min(K, N) + K\logN)*.
Space complexity
The space complexity will be O(N) because, in the worst case, our min-heap will be storing one number from each of the ‘N’ rows.
An Alternate Solution
Since each row and column of the matrix is sorted, is it possible to use Binary Search to find the Kth smallest number?
The biggest problem to use Binary Search, in this case, is that we don’t have a straightforward sorted array, instead, we have a matrix. As we remember, in Binary Search, we calculate the middle index of the search space (‘1’ to ‘N’) and see if our required number is pointed out by the middle index; if not we either search in the lower half or the upper half. In a sorted matrix, we can’t really find a middle. Even if we do consider some index as middle, it is not straightforward to find the search space containing numbers bigger or smaller than the number pointed out by the middle index.
An alternative could be to apply the Binary Search on the “number range” instead of the “index range”. As we know that the smallest number of our matrix is at the top left corner and the biggest number is at the bottom right corner. These two numbers can represent the “range” i.e., the start
and the end
for the Binary Search. Here is how our algorithm will work:
- Start the Binary Search with
start = matrix[0][0]
andend = matrix[n-1][n-1]
. - Find
middle
of thestart
and theend
. Thismiddle
number is NOT necessarily an element in the matrix. - Count all the numbers smaller than or equal to
middle
in the matrix. As the matrix is sorted, we can do this in O(N). - While counting, we can keep track of the “smallest number greater than the
middle
” (let’s call itn1
) and at the same time the “biggest number less than or equal to themiddle
” (let’s call itn2
). These two numbers will be used to adjust the “number range” for the Binary Search in the next iteration. - If the count is equal to ‘K’,
n1
will be our required number as it is the “biggest number less than or equal to themiddle
”, and is definitely present in the matrix. - If the count is less than ‘K’, we can update
start = n2
to search in the higher part of the matrix and if the count is greater than ‘K’, we can updateend = n1
to search in the lower part of the matrix in the next iteration.
Here is the visual representation of our algorithm:
Here is what our algorithm will look like:
1 | class Solution: |
Time complexity
The Binary Search could take O(log(max-min )) iterations where ‘max’ is the largest and ‘min’ is the smallest element in the matrix and in each iteration we take O(N) for counting, therefore, the overall time complexity of the algorithm will be O(N\log(max-min))*.
Space complexity
The algorithm runs in constant space O(1).
Smallest Number Range (Hard)
632. Smallest Range Covering Elements from K Lists Design Gurus Educative.io
Problem Statement
Given ‘M’ sorted arrays, find the smallest range that includes at least one number from each of the ‘M’ lists.
Example 1:
1 | Input: L1=[1, 5, 8], L2=[4, 12], L3=[7, 8, 10] |
Example 2:
1 | Input: L1=[1, 9], L2=[4, 12], L3=[7, 10, 16] |
Solution
This problem follows the K-way merge pattern and we can follow a similar approach as discussed in Merge K Sorted Lists.
We can start by inserting the first number from all the arrays in a min-heap. We will keep track of the largest number that we have inserted in the heap (let’s call it currentMaxNumber
).
In a loop, we’ll take the smallest (top) element from the min-heap andcurrentMaxNumber
has the largest element that we inserted in the heap. If these two numbers give us a smaller range, we’ll update our range. Finally, if the array of the top element has more elements, we’ll insert the next element to the heap.
We can finish searching the minimum range as soon as an array is completed or, in other terms, the heap has less than ‘M’ elements.
Code
Here is what our algorithm will look like:
1 | from heapq import * |
Time complexity
Since, at most, we’ll be going through all the elements of all the arrays and will remove/add one element in the heap in each step, the time complexity of the above algorithm will be O(N\logM)* where ‘N’ is the total number of elements in all the ‘M’ input arrays.
Space complexity
The space complexity will be O(M) because, at any time, our min-heap will be store one number from all the ‘M’ input arrays.
Problem Challenge 1
Similar | Top Interview 150 | 373. Find K Pairs with Smallest Sums Design Gurus Educative.io
K Pairs with Largest Sums (Hard)
Given two sorted arrays in descending order, find ‘K’ pairs with the largest sum where each pair consists of numbers from both the arrays.
Example 1:
1 | Input: L1=[9, 8, 2], L2=[6, 3, 1], K=3 |
Example 2:
1 | Input: L1=[5, 2, 1], L2=[2, -1], K=3 |
Constraints:
- 1 <= nums1.length, nums2.length <= 10^5
- -10^9 <= nums1[i], nums2[i] <= 10^9
nums1
andnums2
both are sorted in non-decreasing order.- 1 <= k <= 10^4
k <= nums1.length * nums2.length
Solution
This problem follows the K-way merge pattern and we can follow a similar approach as discussed in Merge K Sorted Lists.
We can go through all the numbers of the two input arrays to create pairs and initially insert them all in the heap until we have ‘K’ pairs in Min Heap. After that, if a pair is bigger than the top (smallest) pair in the heap, we can remove the smallest pair and insert this pair in the heap.
We can optimize our algorithms in two ways:
- Instead of iterating over all the numbers of both arrays, we can iterate only the first ‘K’ numbers from both arrays. Since the arrays are sorted in descending order, the pairs with the maximum sum will be constituted by the first ‘K’ numbers from both the arrays.
- As soon as we encounter a pair with a sum that is smaller than the smallest (top) element of the heap, we don’t need to process the next elements of the array. Since the arrays are sorted in descending order, we won’t be able to find a pair with a higher sum moving forward.
Code
Here is what our algorithm will look like:
1 | from heapq import * |
Time Complexity
Since, at most, we’ll be going through all the elements of both arrays and we will add/remove one element in the heap in each step, the time complexity of the above algorithm will be O(NMlogK) where ‘N’ and ‘M’ are the total number of elements in both arrays, respectively.
If we assume that both arrays have at least ‘K’ elements then the time complexity can be simplified to O(K^2logK), because we are not iterating more than ‘K’ elements in both arrays.
Space Complexity
The space complexity will be O(K) because, at any time, our Min Heap will be storing ‘K’ largest pairs.