Как эффективно искать в упорядоченной матрице?

У меня есть матрица x by y, где каждая строка и каждый столбец находятся в порядке возрастания, как указано ниже.

1   5   7    9
4   6   10   15
8   11  12   19
14  16  18   21

Как искать эту матрицу для числа в O(x+y)?

Мне задали этот вопрос на собеседование, но я не мог понять, как это сделать. Любопытно узнать, можно ли это сделать.

Ответ 1

Начните с последнего элемента первой строки (верхний правый угол).
Сравните его с key. У нас есть 3 случая:

  • Если они равны, мы закончили.

  • Если key больше, чем этот элемент то это означает, что key не может присутствовать в этой строке перемещайте поиск к элементу под ним.

  • Если key меньше, чем этот элемент, тогда это означает, что key может присутствовать в этом строка влево и не может присутствовать в столбце дальше вниз, поэтому переместите поиск на элемент слева от него.

Продолжайте делать это, пока не найдете элемент, иначе вы не сможете двигаться дальше (ключ не существует).

Псевдокод:

Let R be number of rows
Let C be number of columns

Let i = 0
Let j = C-1

found = false
while( i>=0 && i<R) && (j>=0 && j<C) )
   if (matrix[i][j] == key )
      found = true
      break
   else if( matrix[i][j] > key )
       j--
   else if( matrix[i][j] < key )
       i++
end-while

Ответ 2

Разделить матрицу в 4 подматрицы. Если в нижней правой части подматрицы меньше ключа, отбросьте его. Если левая верхняя часть субматрицы больше, чем ключ, отбросьте ее. Повторите процедуру расщепления для остальных подматриц.

[Обновление] Для некоторого псевдокода (и обсуждения сложности) см. Ответ Джеффри Л. Уитлеу от этого вопроса.

Ответ 3

// the matrix is like this, from left to right is ascending, and
// from down to up is ascending, but the second row'start is not always bigger than the first row end, which is diff from [leetcode]https://oj.leetcode.com/problems/search-a-2d-matrix/
// 1   5   7    9
// 4   6   10   15
// 8   11  12   19
// 14  16  18   21
// time complexity is O(x+y), x is the count of row, and y is the count of column

public boolean searchMatrix2(int[][] matrix, int target) {
    int rowCount = matrix.length;
    if(rowCount == 0) return false;

    int colCount = matrix[0].length;
    if(colCount == 0) return false;

    //first find the target row, needs O(x)
    int targetRow = 0;
    while(targetRow < rowCount-1 && matrix[targetRow+1][0] <= target) {
        targetRow++;
    }
    //than find the target in the target row, needs O(y), so the total is O(x)+O(y)
    boolean result = false;
    for(int i = 0; i < colCount; i ++) {
        if(matrix[targetRow][i] == target) {
            result = true;
            break;
        }
    }
    return result;
}

Собственно, мы можем дважды использовать двоичный поиск, сначала найти целевую строку с помощью двоичного поиска, а затем найти цель в строке двоичным поиском, поэтому сложность времени O (lgx) + O (lgy) равна O ( lgx + lgy), лучше O (x + y).