Подтвердить что ты не робот

Найти элемент в отсортированной матрице

Проблема: Учитывая матрицу, в которой сортируются каждая строка и каждый столбец, напишите метод, чтобы найти в нем элемент.

Это классический вопрос интервью, вот мое решение

boolean F(int[][] matrix, int hs, int he, int ws, int we)
{
    if (hs > he || ws > we) 
        return false; 

    int m = (hs + he) / 2; 
    int n = (ws + we) / 2;

    if (matrix[m][n] == t)
    {
        return true;
    }
    else if (matrix[m][n] < t)
    {
        // find the ele in the same row, right to [m][n]
        F(m, m, n + 1, we);

        // find the ele in the same col, upper to [m][n]
        F(m + 1, he, n, n);

        // find the ele in the area, where i>m,j>n 
        F(m + 1, he, n + 1, we);       
    } 
    else if (matrix[m][n] > t)
    {
        // very similar to previous part
    }
}

Время работы алгоритма: log (m) + log (n). Я ищу алгоритм, который более эффективен или с кратким кодом.

Имея больше комментариев, я придумываю следующий код:

// return target recurrence in the matrix
int F(int[][] m, int rs, int re, int cs, int ce, int t){
   int r1 = rs, r2 = re;
   int c1 = cs, c2 = ce;
   int r=0 , c = c1;

   while( r1 < r2 && c1 < c2 ){
   // find the last element that <= t in column c
     r  = FlastLess( r1, r2, c, t)

     if( r == -1 ) break;

     else{
       // find the first ele in the row that is >=t
       c = FfirstGreater( r, c1, c2, t);

       if( c == -1)  break;
       else{
         r2 = r; 
         c1 = c; 
       }// else    
     }// else 
   }// while
}// f

Вот ссылка на функцию F1 и F2 Найти первый элемент в отсортированном массиве, который больше целевого

void FlastLess(int s, int e, int t){
  int l = s, h = e;
  while( l != h ){
     int mid = (l+h)/2;
     if( mid >=  t) high = mid - 1; 
     else {
       if( high < t) low= mid + 1;
       else low = mid;
     } 
  }

 void FfirstGreater(int s, int e, int t){
  while(l < h){
    mid = (l+h)/2;
    if ( mid <=  t) low = mid+1;
    else high = mid;
  }
 }

}
4b9b3361

Ответ 1

Начните в нижнем левом углу вашей матрицы. Затем идите направо, пока не найдете точное число (сделано), или пока не найдете число, большее.

Затем вы поднимаетесь вверх в матрице, пока не найдете точное число (сделано), или пока не найдете слишком маленькое число.

Затем снова вы переходите вправо,... и так далее, пока не найдете номер или пока не достигнете правой или верхней части вашей матрицы.

Следующие изображения содержат некоторые примеры, используя таблицу Excel, показывающую целевой номер в зеленом цвете, и путь, который следует за желтым цветом.

enter image description here

enter image description here

В последнем примере мы ищем 207, который не находится в матрице:

enter image description here

Это просто алгоритм. Кодирование остается для вас в качестве упражнения: -)

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

Ответ 2

Ваш алгоритм может быть O (log m + log n), но он также дает неправильный ответ.

Предположим, что вы ищете "4" в следующей матрице (где верхний левый - строка = 0, col = 0):

0 1 4
1 2 5
2 3 6

Ваш алгоритм начинается с просмотра 2 в центре. Поскольку 4 больше, чем 2, вы переходите к поиску в той же строке (не там), в том же столбце (не там) и в правом нижнем углу (не там). Упс.

Ограничение, которое сортируются в каждой строке и столбце, на самом деле довольно слабое. В частности, элементы по диагонали могут быть в любом порядке.

Я считаю, что правильный подход состоит в том, чтобы выполнить двоичный поиск в первом и последнем столбцах, чтобы сузить диапазон возможных строк. Затем выполните двоичный поиск в первой и последней из этих строк, чтобы сузить возможные столбцы. И так далее.

Не уверен, как анализировать производительность этого...

Ответ 3

Вот что бы я попробовал. Учитывая матрицу m на n A, сравните значение X со входом A(m/2,n/2) (при необходимости используйте полы).

Если A(m/2,n/2) == X, сделано.

Если A(m/2,n/2) < X, то для проверки есть 3 меньших матрицы:

A(1:m/2, 1:n/2) 
A(1:m/2, n/2+1:n) 
A(m/2+1:m, 1:n/2) 

Если A(m/2,n/2) > X, то для проверки есть 3 меньших матрицы:

A(m/2:m, n/2:n) 
A(1:m/2, n/2+1:n) 
A(m/2+1:m, 1:n/2) 

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

Сложность этого приблизительно равна O((n*m)^0.8).


Сортированная строка и столбец являются частным случаем Young tableau. Я выполнил поиск Google в поисках таблицы Young и нашел эту статью, которая дает 3 приятных подхода (первая (и самая худшая) из которых - одна Я описал выше).

Ответ 4

Для алгоритма, основанного на сравнении, оптимальны запросы O (lg (m) + lg (n)).

Proof

Для запроса на основе сравнения каждый запрос может иметь только два результата: true или false. Очевидным расширением этого является то, что для N запросов вы можете получить не более 2 N результатов. Поэтому, используя N запросов, вы можете находить элементы в матрице с не более чем двумя элементами N.

Сколько запросов требуется для поиска матрицы m x n? Просто решите для N.

2 N= mn
lg (2 N) = lg (mn)
N = lg (m) + lg (n)

Поэтому запросы lg (m) + lg (n) оптимальны.

Запросы без сравнения

Это доказательство является убедительным, но только для запросов на основе сравнения. Если вы запросите матрицу таким образом, чтобы она не включала сравнения, вы можете получить почти постоянное время, если знаете распределение значений. Я не буду давать вам алгоритм, но я бы предложил посмотреть Radix sort, поскольку он содержит методы, не связанные с сравнением, которые требуются для оценки нижней границы lg (m) + lg (n).

Ответ 5

Считая предыдущие комментарии, я придумал этот алгоритм. В принципе, предположим, что, начиная с верхнего правого угла, матрица может использоваться как BST с некоторыми "петлями" (нам не нужны эти петли).

1 4 9
5 6 10

     9
    /  \
   4   10
  / \  /
 1   6
  \ /
   5

Этот алгоритм аналогичен поиску в BST и очень легко понять. Наихудшее время выполнения программы - O (n + m).

public static boolean search( int[][] matrix, int value )
{   
    int rows = matrix.length;
    int columns = matrix[0].length;

    int i = 0;
    int j = columns - 1;

    while( i < rows
           && j >= 0 )
    {   
        if( matrix[i][j] == value )
        {
            System.out.println( "Found at " + i + " " + j );
            return true;
        }

        if( matrix[i][j] < value )
        {
            j--;
        }
        else
        {
            i++;
        }
    }

    return false;
}

Ответ 6

boolean FindElem(int[][] mat, int elem, int M, int N) {
 int row = 0;
 int col = N-1;
 while (row < M && col >= 0) {
   if (mat[row][col] == elem) {
     return true;
   } else if (mat[row][col] > elem) {
     col--;
   } else {
     row++;
   }
 }
   return false;
}

Ответ 7

Я считаю, что ваш алгоритм не имеет временных ограничений, которые вы считаете, что он делает.

Чтобы убедиться в этом, для простоты предположим, что ваша сетка представляет собой n x n квадрат (позвольте назвать этот размер m). Если в этом случае я могу получить другую временную привязку, чем O (log n), могу утверждать, что то, что у вас есть, не должно быть правильным.

В частности, обратите внимание, что в худшем случае вы делаете три рекурсивных вызова на проблемы с размером (n/2) x (n/2) = m/4. Это означает, что мы имеем рекуррентность

T(1) = 1
T(m) = 3T(m / 4) + O(1)

Используя Мастер-теорему, время выполнения для этой функции будет O (m log 4 3) = O (n 2 log 4 3) = O (n log 4 9) & около; О (п 1,5849625). Это & omega; (log n + log m); т.е. асимптотически строго больше.

Как и многие другие люди, существует несколько хорошо известных алгоритмов, которые выполняются в O (m + n), которые основаны на шаге в один шаг в правильном направлении на каждом шаге. Следовательно, правильность в сторону, я бы не советовал использовать алгоритм, который вы опубликовали.

Ответ 8

Указанные элементы в массиве 2d (be [n] [m]) растут горизонтально и вертикально. Поэтому для данного вопроса нам нужно сначала найти индекс элемента. Поэтому, если мы сможем быстрее найти элемент, мы сможем оптимизировать решение. Вопрос в том, как мы находим его эффективным способом. Один подход - взять средний элемент матрицы и проверить данный элемент с ним

если данный элемент меньше среднего элемента, то наше решение лежит в матрице a [0] [0] на [n/2] [m/2], поскольку все элементы справа и внизу больше среднего (так как данный элемент меньше среднего элемента), поэтому мы сократили наше пространство поиска от [n] [m] до [n/2] [m/2], которое составляет одну четверть от исходного размера.

если данный элемент больше среднего элемента, тогда наше решение не лежит в матрицах a [0] [0] до [n/2] [m/2], потому что все элементы слева и сверху меньше среднего (так как данный элемент больше среднего элемента), поэтому наше пространство поиска представляет собой полный массив минус [0] [0] на [n/2] [m/2], который составляет три четверти от исходного размера. Общий массив минус a [0] [0] на [n/2] [m/2] означает, что будет три рекурсивных вызова с индексом массива

--------->a[0][m/2](start index) to a[n/2][m](end index)  
--------->a[n/2][0](start index) to a[n][m/2](end index)
--------->a[n/2][m/2](start index) to a[n][m](end index)

Теперь рекурсивно вызовите ту же функцию, что и на нашем поисковом пространстве.

Сложность времени нашей функции будет следующей. ПРИМЕЧАНИЕ. Во временной функции n представляет общее число элементов, но не число строк, как указано .n = (no_of_rows) * (no_of_columns)

                _________________T(n/4)  if given element is less than middle of the array.
               / 
              /
T(n)==========------------------- 1 if n=1 (if element found)
              \
               \_________________3T(n/4) if given element is greater than middle element of array

поэтому функция времени ожидания будет

T (n) = 3T (n/4) или T (n) = T (n/4)

In worst case T(n)=3T(n/4)
               T(n)=3{3T(n/4)}
               T(n)=3power(i)T(n/(4)poweri)     equation------> (1) 

Но T (1) = 1 (угадывание данного элемета найдено в массиве)

so  n/(4power(i))=1
====> n=2power(2*i)
====> n=2power(2*i)
Talking log to base 2 on both sides (log[n])/2=i ====> i=log(sqrt(n))

подставляя уравнение 1, получим

T(n)=3power(log[sqrt(n)])
T(n)∈ θ( nlog sqrt(3) )..

Насколько я знаю, это алгоритм, который берет минимальное количество сравнений.

Ответ 9

Решение для JavaScript:

//start from the top right corner
//if value = el, element is found
//if value < el, move to the next row, element can't be in that row since row is sorted
//if value > el, move to the previous column, element can't be in that column since column is sorted

function find(matrix, el) {

  var row = 0; //first row
  var col = matrix[0].length - 1; //last column

  while (row < matrix.length && col >= 0) {
    if (matrix[row][col] === el) { //element is found
      return true;
    } else if (matrix[row][col] < el) {
      row++; //move to the next row
    } else {
      col--; //move to the previous column
    }
  }

  return false;

}