๐ Problem Statement
Given an n x n matrix where each of the rows and columns are sorted in ascending order, find the kth smallest element in the matrix.
Example:
python
Input: matrix = [
[1, 5, 9],
[10, 11, 13],
[12, 13, 15]
], k = 8
Output: 13
Explanation: The elements of the matrix in ascending order are [1, 5, 9, 10, 11, 12, 13, 13, 15], and the 8th smallest element is 13.
๐ง Key Insight
The matrix is sorted both row-wise and column-wise, which allows us to optimize the search for the kth smallest element using binary search. Instead of brute-forcing through all elements, we can use binary search to narrow down the potential values for the kth smallest element.
Steps:
- Binary Search Setup: We use binary search on the value of the matrix elements rather than their indices. The smallest element in the matrix is
matrix[0][0], and the largest element is matrix[n-1][n-1].
- Count Function: We need a helper function to count how many elements in the matrix are less than or equal to a given value. This count helps us decide the next search bounds.
- Binary Search: We adjust the search range based on the number of elements smaller than or equal to the midpoint of our search range. If the count is less than
k, the target value is too small, and we move the search range upwards; if it's greater than or equal to k, we move the search range downwards.
๐ก Approach
- Binary Search on Value Range: The minimum value in the matrix is
matrix[0][0], and the maximum value is matrix[n-1][n-1].
- Counting Function: Given a value
mid, count how many elements in the matrix are less than or equal to mid. This can be done efficiently by iterating through each row and using a two-pointer technique or binary search within each row.
- Binary Search Loop: Perform binary search on the value range, updating the bounds based on the count.
๐ Python Code
python
class Solution:
def kthSmallest(self, matrix: list[list[int]], k: int) -> int:
n = len(matrix)
# Function to count the number of elements less than or equal to mid
def count_less_equal(mid):
count, row, col = 0, n - 1, 0
while row >= 0 and col < n:
if matrix[row][col] <= mid:
count += row + 1 # All elements in this row up to col are <= mid
col += 1
else:
row -= 1
return count
# Binary search for the kth smallest element
left, right = matrix[0][0], matrix[n - 1][n - 1]
while left < right:
mid = (left + right) // 2
if count_less_equal(mid) < k:
left = mid + 1
else:
right = mid
return left
๐ Step-by-Step Explanation
1. Binary Search Setup
python
left, right = matrix[0][0], matrix[n - 1][n - 1]
- We initialize the binary search range using the smallest and largest elements in the matrix, i.e., the first element (
matrix[0][0]) and the last element (matrix[n-1][n-1]).
2. Counting Function
python
def count_less_equal(mid):
count, row, col = 0, n - 1, 0
while row >= 0 and col < n:
if matrix[row][col] <= mid:
count += row + 1 # All elements in this row up to col are <= mid
col += 1
else:
row -= 1
return count
- The
count_less_equal(mid) function counts how many elements in the matrix are less than or equal to the value mid.
- We use two pointers,
row starting from the bottom-left corner (n-1, 0), and col starting from the top-left.
- If
matrix[row][col] <= mid, then all elements from matrix[row][0] to matrix[row][col] are also less than or equal to mid. We add row + 1 to the count (since there are row + 1 elements in this row that satisfy the condition) and move to the next column.
- Otherwise, we move the pointer up to check the previous row.
3. Binary Search Loop
python
while left < right:
mid = (left + right) // 2
if count_less_equal(mid) < k:
left = mid + 1
else:
right = mid
- We perform binary search on the value range
[left, right]. For each midpoint mid, we call count_less_equal(mid) to count how many elements are less than or equal to mid.
- If the count is less than
k, we know that the kth smallest element is larger than mid, so we move the left pointer up.
- If the count is greater than or equal to
k, we move the right pointer down to narrow down the search space.
4. Return the Result
python
return left
- Once the binary search loop ends, the value of
left will be the kth smallest element, which we return.
๐ก Example Walkthrough
python
Input:
matrix = [
[1, 5, 9],
[10, 11, 13],
[12, 13, 15]
], k = 8
Output: 13
- The matrix is sorted row-wise and column-wise.
- The elements in ascending order are:
[1, 5, 9, 10, 11, 12, 13, 13, 15]
- The 8th smallest element is
13.
โฑ๏ธ Time & Space Complexity
MetricComplexityTime ComplexityO(n * log(max - min))Space ComplexityO(1)
- Time Complexity:
- The binary search runs in
O(log(max - min)) time, where max and min are the largest and smallest elements in the matrix.
- For each midpoint
mid, we run the count_less_equal function, which takes O(n) time (since it iterates over at most n rows and columns).
- Thus, the total time complexity is
O(n * log(max - min)).
- Space Complexity: We use constant space for the binary search and counting function, so the space complexity is
O(1).
๐งต Final Thoughts
The solution to LeetCode 378 makes good use of binary search and counting elements efficiently to narrow down the search space for the kth smallest element in a sorted matrix. This is a classic example of how binary search can be applied to optimize problems involving sorted arrays or matrices.
- Binary Search: Instead of searching the matrix linearly, binary search allows us to find the kth smallest element efficiently.
- Efficient Counting: By counting elements less than or equal to the midpoint, we can adjust our search bounds dynamically.
This approach is both time-efficient and space-efficient, making it a great choice for solving problems related to sorted matrices.