Skip to content

Commit 6b88129

Browse files
committed
feat(algorithms, k-wary-merge): kth smallest element in sorted matrix
1 parent 7044acc commit 6b88129

16 files changed

+213
-1
lines changed

algorithms/k_way_merge/__init__.py

Whitespace-only changes.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Kth Smallest Element in a Sorted Matrix
2+
3+
Given an n x n matrix where each of the rows and columns is sorted in ascending order, return the kth smallest element
4+
in the matrix.
5+
6+
Note that it is the kth smallest element in the sorted order, not the kth distinct element.
7+
8+
You must find a solution with a memory complexity better than O(n^2).
9+
10+
## Examples
11+
12+
![Example 1](./images/examples/kth_smallest_element_in_matrix_example_1.png)
13+
![Example 2](./images/examples/kth_smallest_element_in_matrix_example_2.png)
14+
![Example 3](./images/examples/kth_smallest_element_in_matrix_example_3.png)
15+
16+
Example 4:
17+
18+
```text
19+
Input: matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8
20+
Output: 13
21+
Explanation: The elements in the matrix are [1,5,9,10,11,12,13,13,15], and the 8th smallest number is 13
22+
```
23+
24+
Example 5:
25+
```text
26+
Input: matrix = [[-5]], k = 1
27+
Output: -5
28+
```
29+
30+
## Constraints
31+
32+
- n == `matrix.length` == `matrix[i].length`
33+
- 1 <= n <= 300
34+
- -10^9 <= `matrix[i][j]` <= 10^9
35+
- All the rows and columns of matrix are guaranteed to be sorted in non-decreasing order.
36+
- 1 <= k <= n^2
37+
38+
## Topics
39+
40+
- Binary Search
41+
- Sorting
42+
- Heap (Priority Queue)
43+
- Matrix
44+
45+
## Solution
46+
47+
A key observation when tackling this problem is that the matrix is sorted along rows and columns. This means that whether
48+
we look at the matrix as a collection of rows or as a collection of columns, we see a collection of sorted lists.
49+
50+
As we know, the k way merge pattern merges k-sorted arrays into a single sorted array using a heap. Therefore, to find
51+
the `kth` smallest element in the matrix, we will use the same method where we will deal with the rows of the matrix as
52+
k sorted arrays. So, this approach uses a min-heap and inserts the first element of each matrix row into the min-heap
53+
(along with their respective row and column indexes for tracking). It then removes the top element of the heap(smallest
54+
element) and checks whether the element has any next element in its row. If it has, that element is added to the heap.
55+
This is repeated until k elements have been removed from the heap. The `kth` element removed is the `kth` smallest
56+
element of the entire matrix.
57+
58+
Here’s how we implement our algorithm using a min-heap to find the `kth` smallest element in a sorted matrix:
59+
60+
1. We push the first element of each row of the matrix in the min-heap, storing each element along with its row and
61+
column index.
62+
2. Remove the top (root) of the min-heap.
63+
3. If the popped element has the next element in its row, push the next element in the heap.
64+
4. Repeat steps 2 and 3 as long as there are elements in the min-heap, and stop as soon as we’ve popped k elements from
65+
it.
66+
5. The last popped element in this process is the `kth` smallest element in the matrix.
67+
68+
![Solution 1](./images/solutions/kth_smallest_element_in_matrix_solution_1.png)
69+
![Solution 2](./images/solutions/kth_smallest_element_in_matrix_solution_2.png)
70+
![Solution 3](./images/solutions/kth_smallest_element_in_matrix_solution_3.png)
71+
![Solution 4](./images/solutions/kth_smallest_element_in_matrix_solution_4.png)
72+
![Solution 5](./images/solutions/kth_smallest_element_in_matrix_solution_5.png)
73+
![Solution 6](./images/solutions/kth_smallest_element_in_matrix_solution_6.png)
74+
![Solution 7](./images/solutions/kth_smallest_element_in_matrix_solution_7.png)
75+
![Solution 8](./images/solutions/kth_smallest_element_in_matrix_solution_8.png)
76+
77+
### Time Complexity
78+
79+
The time complexity of the first step is:
80+
81+
- `O(min(n,k))` for iterating over whichever is the minimum of both, where n is the size of the matrix and k is the smallest
82+
element we need to find.
83+
- The push operation takes `O(log(m))` time, where m is the number of elements currently in the heap. However, since we’re
84+
adding elements only `min(n,k)` elements, therefore, the time complexity of the first loop is `O(min(n,k)×log(min(n,k)))`
85+
- In the while-loop, we pop and push `m` elements in the heap until we find the `kth` smallest element. In the worst case,
86+
the heap could have up to `min(n,k)` elements. Therefore, the time complexity of this step is `O(klog(min(n,k)))`
87+
88+
Overall, the total time complexity of this solution is `O((min(n,k)+k)×log(min(n,k)))`
89+
90+
### Space Complexity
91+
92+
The space complexity is O(n), where n is the total number of elements in the min-heap.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from typing import List, Tuple
2+
import heapq
3+
4+
5+
def kth_smallest_in_matrix_with_heap_1(matrix: List[List[int]], k: int) -> int:
6+
"""
7+
Finds the kth smallest element in a matrix that has its rows and columns sorted in ascending order.
8+
Args:
9+
matrix (List[List[int]]): The matrix to find the kth smallest element in.
10+
k (int): The kth smallest element.
11+
Returns:
12+
int: The kth smallest element.
13+
"""
14+
if not matrix:
15+
return -1
16+
17+
min_heap: List[int] = []
18+
19+
for lst in matrix:
20+
for element in lst:
21+
heapq.heappush(min_heap, element)
22+
23+
counter = k - 1
24+
25+
while counter > 0:
26+
heapq.heappop(min_heap)
27+
counter -= 1
28+
29+
return min_heap[0]
30+
31+
32+
def kth_smallest_in_matrix_with_heap_2(matrix: List[List[int]], k: int) -> int:
33+
"""
34+
Finds the kth smallest element in a matrix that has its rows and columns sorted in ascending order.
35+
Args:
36+
matrix (List[List[int]]): The matrix to find the kth smallest element in.
37+
k (int): The kth smallest element.
38+
Returns:
39+
int: The kth smallest element.
40+
"""
41+
# storing the number of rows in the matrix to use it in later
42+
row_count = len(matrix)
43+
# declaring a min-heap to keep track of smallest elements
44+
min_numbers: List[Tuple[int, int, int]] = []
45+
46+
for index in range(min(row_count, k)):
47+
# pushing the first element of each row in the min-heap
48+
# The heappush() method pushes an element into an existing heap
49+
# in such a way that the heap property is maintained.
50+
first_element = matrix[index][0]
51+
element_index = 0
52+
heapq.heappush(min_numbers, (first_element, index, element_index))
53+
54+
numbers_checked, smallest_element = 0, 0
55+
# iterating over the elements pushed in our min-heap
56+
while min_numbers:
57+
# get the smallest number from top of heap and its corresponding row and column
58+
smallest_element, row_index, col_index = heapq.heappop(min_numbers)
59+
numbers_checked += 1
60+
# when numbers_checked equals k, we'll return smallest_element
61+
if numbers_checked == k:
62+
break
63+
# if the current popped element has a next element in its row,
64+
# add the next element of that row to the min-heap
65+
if col_index + 1 < len(matrix[row_index]):
66+
heapq.heappush(
67+
min_numbers,
68+
(matrix[row_index][col_index + 1], row_index, col_index + 1),
69+
)
70+
71+
# return the Kth smallest element found in the matrix
72+
return smallest_element
79.7 KB
Loading
75 KB
Loading
94.2 KB
Loading
44.6 KB
Loading
54.8 KB
Loading
42.3 KB
Loading
47.9 KB
Loading

0 commit comments

Comments
 (0)