{x}
blog image

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(n2).

 

Example 1:

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 2:

Input: matrix = [[-5]], k = 1
Output: -5

 

Constraints:

  • n == matrix.length == matrix[i].length
  • 1 <= n <= 300
  • -109 <= matrix[i][j] <= 109
  • All the rows and columns of matrix are guaranteed to be sorted in non-decreasing order.
  • 1 <= k <= n2

 

Follow up:

  • Could you solve the problem with a constant memory (i.e., O(1) memory complexity)?
  • Could you solve the problem in O(n) time complexity? The solution may be too advanced for an interview but you may find reading this paper fun.

Solution Explanation:

This problem asks to find the kth smallest element in a sorted matrix. The matrix is n x n, and each row and column is sorted in ascending order. A naive approach would be to flatten the matrix and sort it, but this would have O(n²) space complexity, violating the problem's constraint. A more efficient solution uses binary search.

Approach:

The core idea is to perform a binary search on the possible range of values in the matrix. The search space is bounded by the minimum element (matrix[0][0]) and the maximum element (matrix[n-1][n-1]).

For each mid value during the binary search, we need to efficiently count how many elements in the matrix are less than or equal to mid. This is done using a clever two-pointer approach. We start from the bottom-left corner of the matrix. If the current element is less than or equal to mid, it means all the elements in the row above are also less than or equal to mid. So, we add the number of elements in that row (i + 1) to our count and move to the right column (j++). Otherwise, if the current element is greater than mid, it means all elements to the right in this row are also greater than mid, so we move up (i--). This process efficiently counts elements less than or equal to mid without explicitly iterating through the entire matrix.

After counting, we compare the count with k. If count is greater than or equal to k, it means the kth smallest element is less than or equal to mid, so we continue searching in the lower half (right = mid). Otherwise, the kth smallest element is greater than mid, and we search in the upper half (left = mid + 1). The binary search continues until left and right converge, at which point left (or right) holds the kth smallest element.

Time Complexity Analysis:

  • Binary Search: The binary search takes O(log(R)), where R is the range of values in the matrix (approximately O(log(n²)) or O(2log(n)) which simplifies to O(log n)).
  • Counting Elements: The two-pointer approach for counting elements takes O(n) time in the worst case (when traversing a diagonal).

Therefore, the overall time complexity is O(n log n).

Space Complexity Analysis:

The solution uses only a constant amount of extra space for variables, regardless of the input matrix size. Thus, the space complexity is O(1).

Code Explanation (Python):

The Python code implements the described algorithm. Let's break it down:

class Solution:
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        def check(matrix, mid, k, n):  # Helper function to count elements <= mid
            count = 0
            i, j = n - 1, 0
            while i >= 0 and j < n:
                if matrix[i][j] <= mid:
                    count += i + 1
                    j += 1
                else:
                    i -= 1
            return count >= k
 
        n = len(matrix)
        left, right = matrix[0][0], matrix[n - 1][n - 1] # Initialize search space
        while left < right:  # Binary search
            mid = (left + right) >> 1
            if check(matrix, mid, k, n):
                right = mid
            else:
                left = mid + 1
        return left # left is the kth smallest element
 

The check function efficiently counts the number of elements less than or equal to mid using the two-pointer technique as explained above. The main function performs the binary search using this helper function.

The Java, C++, and Go codes implement the same algorithm with minor syntax differences. They all achieve the same time and space complexity.