Median of a Row-wise Sorted Matrix

hard Matrix (2D arrays)

Problem

A matrix has r rows and c columns, with r * c odd. Each row is sorted from small to large (the columns are not sorted). Print the median of all the numbers: the middle one after sorting all r * c numbers.

Constraints

  • 1 <= r, c <= 100
  • r * c is odd
  • 1 <= value <= 10^9

Examples

Input

3 3
1 3 5
2 6 9
3 6 9

Output

5

Solutions

Best approachTime O(r log(c) log(range))Space O(1)

Sorting everything costs O(r c log(r c)). Instead we guess a value and count how many numbers are at most that value, using the sorted rows. The median is the smallest value that has more than half of the numbers at or below it.

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * Problem : Find the median.
 * Input   : r, c and the rows.
 * Output  : The median.
 * Approach: Binary search on the value
 * Time    : O(r log(c) log(range))
 * Space   : O(1)
 * Java    : 8 or newer.
 */
import java.util.Scanner;

public class Main {
    // This method solves the task. It receives the input and returns the answer.
    static long solve(int[][] m) {
        int rows = m.length;
        int cols = m[0].length;
        // Step 1: The median is a value between the smallest and the biggest number. Search for it with binary search on the value.
        long low = Long.MAX_VALUE;
        long high = Long.MIN_VALUE;
        for (int[] row : m) {
            low = Math.min(low, row[0]);
            high = Math.max(high, row[cols - 1]);
        }
        long wanted = (long) rows * cols / 2;
        while (low < high) {
            long mid = low + (high - low) / 2;
            // Step 2: Count how many numbers are <= mid. Every row is sorted, so a binary search per row is enough.
            long count = 0;
            for (int[] row : m) {
                int left = 0;
                int right = cols;
                while (left < right) {
                    int middle = (left + right) / 2;
                    if (row[middle] <= mid) {
                        left = middle + 1;
                    } else {
                        right = middle;
                    }
                }
                count += left;
            }
            // Step 3: The median is the smallest value with more than "wanted" numbers at or below it.
            if (count <= wanted) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        return low;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int rows = scanner.nextInt();
        int cols = scanner.nextInt();
        int[][] m = new int[rows][cols];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                m[i][j] = scanner.nextInt();
            }
        }
        System.out.println(solve(m));
    }
}
Hints (2)
  1. How can sorted rows count fast?
  2. What is monotonic here?