-
Notifications
You must be signed in to change notification settings - Fork 2
feat(algorithms, k-way-merge): kth smallest element in sorted matrix #192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
92 changes: 92 additions & 0 deletions
92
algorithms/k_way_merge/kth_smallest_element_in_matrix/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| # Kth Smallest Element in a Sorted Matrix | ||
|
|
||
| Given an n x n matrix where each of the rows and columns is sorted in ascending order, return the kth smallest element | ||
| in the matrix. | ||
|
|
||
| Note that it is the kth smallest element in the sorted order, not the kth distinct element. | ||
|
|
||
| You must find a solution with a memory complexity better than O(n^2). | ||
|
|
||
| ## Examples | ||
|
|
||
|  | ||
|  | ||
|  | ||
|
|
||
| Example 4: | ||
|
|
||
| ```text | ||
| Input: matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8 | ||
| Output: 13 | ||
| Explanation: The elements in the matrix are [1,5,9,10,11,12,13,13,15], and the 8th smallest number is 13 | ||
| ``` | ||
|
|
||
| Example 5: | ||
| ```text | ||
| Input: matrix = [[-5]], k = 1 | ||
| Output: -5 | ||
| ``` | ||
|
|
||
| ## 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 | ||
|
|
||
| ## Topics | ||
|
|
||
| - Binary Search | ||
| - Sorting | ||
| - Heap (Priority Queue) | ||
| - Matrix | ||
|
|
||
| ## Solution | ||
|
|
||
| A key observation when tackling this problem is that the matrix is sorted along rows and columns. This means that whether | ||
| we look at the matrix as a collection of rows or as a collection of columns, we see a collection of sorted lists. | ||
|
|
||
| As we know, the k way merge pattern merges k-sorted arrays into a single sorted array using a heap. Therefore, to find | ||
| the `kth` smallest element in the matrix, we will use the same method where we will deal with the rows of the matrix as | ||
| k sorted arrays. So, this approach uses a min-heap and inserts the first element of each matrix row into the min-heap | ||
| (along with their respective row and column indexes for tracking). It then removes the top element of the heap(smallest | ||
| element) and checks whether the element has any next element in its row. If it has, that element is added to the heap. | ||
| This is repeated until k elements have been removed from the heap. The `kth` element removed is the `kth` smallest | ||
| element of the entire matrix. | ||
|
|
||
| Here’s how we implement our algorithm using a min-heap to find the `kth` smallest element in a sorted matrix: | ||
|
|
||
| 1. We push the first element of each row of the matrix in the min-heap, storing each element along with its row and | ||
| column index. | ||
| 2. Remove the top (root) of the min-heap. | ||
| 3. If the popped element has the next element in its row, push the next element in the heap. | ||
| 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 | ||
| it. | ||
| 5. The last popped element in this process is the `kth` smallest element in the matrix. | ||
|
|
||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|
|
||
| ### Time Complexity | ||
|
|
||
| The time complexity of the first step is: | ||
|
|
||
| - `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 | ||
| element we need to find. | ||
| - The push operation takes `O(log(m))` time, where m is the number of elements currently in the heap. However, since we’re | ||
| adding elements only `min(n,k)` elements, therefore, the time complexity of the first loop is `O(min(n,k)×log(min(n,k)))` | ||
| - In the while-loop, we pop and push `m` elements in the heap until we find the `kth` smallest element. In the worst case, | ||
| the heap could have up to `min(n,k)` elements. Therefore, the time complexity of this step is `O(klog(min(n,k)))` | ||
|
|
||
| Overall, the total time complexity of this solution is `O((min(n,k)+k)×log(min(n,k)))` | ||
|
|
||
| ### Space Complexity | ||
|
|
||
| The space complexity is O(n), where n is the total number of elements in the min-heap. | ||
72 changes: 72 additions & 0 deletions
72
algorithms/k_way_merge/kth_smallest_element_in_matrix/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| from typing import List, Tuple | ||
| import heapq | ||
|
|
||
|
|
||
| def kth_smallest_in_matrix_with_heap_1(matrix: List[List[int]], k: int) -> int: | ||
| """ | ||
| Finds the kth smallest element in a matrix that has its rows and columns sorted in ascending order. | ||
| Args: | ||
| matrix (List[List[int]]): The matrix to find the kth smallest element in. | ||
| k (int): The kth smallest element. | ||
| Returns: | ||
| int: The kth smallest element. | ||
| """ | ||
BrianLusina marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if not matrix: | ||
| return -1 | ||
|
|
||
| min_heap: List[int] = [] | ||
|
|
||
| for lst in matrix: | ||
| for element in lst: | ||
| heapq.heappush(min_heap, element) | ||
|
|
||
| counter = k - 1 | ||
|
|
||
| while counter > 0: | ||
| heapq.heappop(min_heap) | ||
| counter -= 1 | ||
|
|
||
| return min_heap[0] | ||
|
|
||
|
|
||
| def kth_smallest_in_matrix_with_heap_2(matrix: List[List[int]], k: int) -> int: | ||
| """ | ||
| Finds the kth smallest element in a matrix that has its rows and columns sorted in ascending order. | ||
| Args: | ||
| matrix (List[List[int]]): The matrix to find the kth smallest element in. | ||
| k (int): The kth smallest element. | ||
| Returns: | ||
| int: The kth smallest element. | ||
| """ | ||
| # storing the number of rows in the matrix to use it in later | ||
| row_count = len(matrix) | ||
| # declaring a min-heap to keep track of smallest elements | ||
| min_numbers: List[Tuple[int, int, int]] = [] | ||
|
|
||
| for index in range(min(row_count, k)): | ||
| # pushing the first element of each row in the min-heap | ||
| # The heappush() method pushes an element into an existing heap | ||
| # in such a way that the heap property is maintained. | ||
| first_element = matrix[index][0] | ||
| element_index = 0 | ||
| heapq.heappush(min_numbers, (first_element, index, element_index)) | ||
|
|
||
| numbers_checked, smallest_element = 0, 0 | ||
| # iterating over the elements pushed in our min-heap | ||
| while min_numbers: | ||
| # get the smallest number from top of heap and its corresponding row and column | ||
| smallest_element, row_index, col_index = heapq.heappop(min_numbers) | ||
| numbers_checked += 1 | ||
| # when numbers_checked equals k, we'll return smallest_element | ||
| if numbers_checked == k: | ||
| break | ||
| # if the current popped element has a next element in its row, | ||
| # add the next element of that row to the min-heap | ||
| if col_index + 1 < len(matrix[row_index]): | ||
| heapq.heappush( | ||
| min_numbers, | ||
| (matrix[row_index][col_index + 1], row_index, col_index + 1), | ||
| ) | ||
|
|
||
| # return the Kth smallest element found in the matrix | ||
| return smallest_element | ||
BrianLusina marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Binary file added
BIN
+79.7 KB
..._element_in_matrix/images/examples/kth_smallest_element_in_matrix_example_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+75 KB
..._element_in_matrix/images/examples/kth_smallest_element_in_matrix_example_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+94.2 KB
..._element_in_matrix/images/examples/kth_smallest_element_in_matrix_example_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+44.6 KB
...lement_in_matrix/images/solutions/kth_smallest_element_in_matrix_solution_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+54.8 KB
...lement_in_matrix/images/solutions/kth_smallest_element_in_matrix_solution_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+42.3 KB
...lement_in_matrix/images/solutions/kth_smallest_element_in_matrix_solution_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+47.9 KB
...lement_in_matrix/images/solutions/kth_smallest_element_in_matrix_solution_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+64.7 KB
...lement_in_matrix/images/solutions/kth_smallest_element_in_matrix_solution_5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+65.9 KB
...lement_in_matrix/images/solutions/kth_smallest_element_in_matrix_solution_6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+70.6 KB
...lement_in_matrix/images/solutions/kth_smallest_element_in_matrix_solution_7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+78.7 KB
...lement_in_matrix/images/solutions/kth_smallest_element_in_matrix_solution_8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions
48
algorithms/k_way_merge/kth_smallest_element_in_matrix/test_kth_smallest_element_in_matrix.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| import unittest | ||
| from typing import List | ||
|
|
||
| from parameterized import parameterized | ||
| from algorithms.k_way_merge.kth_smallest_element_in_matrix import ( | ||
| kth_smallest_in_matrix_with_heap_1, | ||
| kth_smallest_in_matrix_with_heap_2, | ||
| ) | ||
|
|
||
| KTH_SMALLEST_IN_SORTED_MATRIX_TEST_CASES = [ | ||
| ([[1, 5, 9], [10, 11, 13], [12, 13, 15]], 8, 13), | ||
| ([[-5]], 1, -5), | ||
| ([[2, 6, 8], [3, 7, 10], [5, 8, 11]], 3, 5), | ||
| ([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 4, 4), | ||
| ([[1, 4], [2, 5]], 4, 5), | ||
| ([[1, 1, 1], [1, 1, 1], [1, 1, 1]], 5, 1), | ||
| ( | ||
| [ | ||
| [1, 3, 5, 7, 9], | ||
| [2, 4, 6, 8, 10], | ||
| [11, 13, 15, 17, 19], | ||
| [12, 14, 16, 18, 20], | ||
| [21, 22, 23, 24, 25], | ||
| ], | ||
| 11, | ||
| 11, | ||
| ), | ||
| ] | ||
|
|
||
|
|
||
| class KthSmallestInMatrixTestCase(unittest.TestCase): | ||
| @parameterized.expand(KTH_SMALLEST_IN_SORTED_MATRIX_TEST_CASES) | ||
| def test_kth_smallest_in_matrix_1( | ||
| self, matrx: List[List[int]], k: int, expected: int | ||
| ): | ||
| actual = kth_smallest_in_matrix_with_heap_1(matrx, k) | ||
| self.assertEqual(expected, actual) | ||
|
|
||
| @parameterized.expand(KTH_SMALLEST_IN_SORTED_MATRIX_TEST_CASES) | ||
| def test_kth_smallest_in_matrix_2( | ||
| self, matrx: List[List[int]], k: int, expected: int | ||
| ): | ||
| actual = kth_smallest_in_matrix_with_heap_2(matrx, k) | ||
| self.assertEqual(expected, actual) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.