2Brute2Best
🔥0

Kth Smallest Element in a Sorted Matrix

Medium·LeetCode #378Basic Anchor
LeetCode
30-Second Mental ModelK-way Merge — kth across sorted rows (min-heap of row fronts)
Complexity:O(N² log N)→O(K log min(N, K))
Key Insight

Treat each row as a sorted list. Keep a min-heap with one cell per row: the first cell of that row you have not used yet. The smallest cell in the heap is the next smallest value in the matrix. Take it out, then push the cell to its right in the same row. The next smallest value is always the front of some row.

Core Validity Invariant

The smallest unused value in the whole matrix is always the smallest front in the heap, because each row's unused cells are sorted and its front is the smallest of them.

01

Problem

You get an n × n matrix. Every row is sorted from smallest to largest, and every column is sorted from top to bottom. Values can repeat.

Return the k-th smallest value in the whole matrix. Repeats count as separate places: if the value 13 appears twice, the two copies take two places in the order.

Input

An n × n integer matrix matrix with sorted rows and columns, and an integer k.

Output

One integer: the k-th smallest value among all n² cells.

Example 1
Input: matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8
Output: 13

Read all nine values in order: 1, 5, 9, 10, 11, 12, 13, 13, 15. The 8th one is 13.

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

There is only one cell, so it is the 1st smallest.

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
  • • Follow-up: Could you solve the problem with a constant memory (i.e., O(1) memory complexity)?
  • • Follow-up: Could you solve the problem in O(n) time complexity? (very advanced; not expected in interviews)
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    Each row is sorted. Where could the smallest value in the whole matrix be?

  2. 2.

    If I read the matrix row after row, is that one sorted list? Check it with the 9 and the 10 in the first example.

  3. 3.

    Do I need every value in sorted order, or only the k-th one?

  4. 4.

    After I use one cell from a row, which cell in that row could be the next candidate?

  5. 5.

    When k is small, do I need to look at all n rows to find the answer?

03

Brute force approach

04

Better approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceCopy all n² values into one list, sort it, and read position kO(N² log N)O(N²)
BetterScan every cell and keep a max-heap of the k smallest seen; its top is the answerO(N² log K)O(K)
OptimalMin-heap with one front cell per row; take k values out and push the cell to the rightO(K log min(N, K))O(min(N, K))
09

Why does this belong to this pattern?

Clues in the problem statement
  • “each row is sorted”Every row is already a sorted list. We can merge the rows instead of sorting them all again.
  • “k-th smallest”Only the first k values taken out in order matter. Stop after k pops.
  • “rows and columns both sorted”Column 0 is sorted too, so the first cells of the rows let us decide how many rows to start.
k sorted lists+k-th smallest across all of them+min-heap of one front per list→K-way Merge — kth across sorted rows (min-heap of row fronts)

This is the anchor of the Kth-across sub-pattern. The rows are sorted lists, so the next smallest value is always the front of some row, and a heap finds it. Unlike Top K, the heap is not a filter on all values: it holds one cell per row, and the pops themselves put the values in order. The next question keeps the same heap but the lists are the pairs of two arrays.

10

Don't make this mistake

Treating the matrix as one sorted list, row after row (pattern confusion)
✗ return matrix[(k - 1) / n][(k - 1) % n];
✓ Take values out of a K-way merge k times, as in the optimal approach.

Rows are sorted, but row 1 does not continue where row 0 stops. On [[1,5,9],[10,11,13],[12,13,15]] with k = 6, the formula reads matrix[1][2] = 13. The correct answer is 12, which sits in row 2. Row 2's first cell is smaller than the end of row 1, so a row-by-row count skips it.

Using a max-heap for the K-way merge (comparator direction)
✗ priority_queue<tuple<int, int, int>> pq;
✓ priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<tuple<int, int, int>>> pq;

A default priority_queue pops the largest value first. Seeded with the fronts 1, 10 and 12, the first pop is 12 (row 2), not 1, so the values come out from the largest end and the answer is wrong.

No bounds check on the cell to the right
✗ pq.push({matrix[r][c + 1], r, c + 1});
✓ if (c + 1 < n) pq.push({matrix[r][c + 1], r, c + 1});

When a row's last cell is taken out, c + 1 = n is past the end of the row. The read is out of bounds, and the heap gets a garbage value.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like K-way Merge translate into production architectures and low-level memory mechanics.