searching in 2d array as O(n) with unsorted rows

后端 未结 3 1539
暖寄归人
暖寄归人 2021-01-19 11:54

I need to write a method that takes 2d array \'int [][] m\' and a value \'val\' and check if val is in the array in the complexity of O(n) while n defined as the num

3条回答
  •  走了就别回头了
    2021-01-19 12:17

    It is possible iff. the matrix m is a square matrix of size n x n. Core idea is inspired by oleg.cherednik's answer. As soon as we find a row in m, such that m[row][0] >= val, we know that val must be in either row row or row - 1(since the same comparison on row - 1 was false). Thus, we have to find our candidate rows (O(n)) and then analyze only those two rows (also O(n)). If m is not square, but rectangular, the algorithm has a complexity of O(n + k), where n is the number of rows and k is the number of colums in m. This leads to the following algorithm.

    public class Test {
    
      public static boolean contains(final int[][]m, final int value) {
        int candidateRow = m.length;
        for (int row = 1; row < m.length; ++row) {
          if (m[row][0] == value) {
            return true;
          }
          if (m[row][0] > value) {
            candidateRow = row;
            break;
          }
        }
    
        for (int val : m[candidateRow - 1]) {
          if (val == value) {
            return true;
          }
        }
    
        if (candidateRow < m.length) {
          for (int val : m[candidateRow]) {
            if (val == value) {
              return true;
            }
          }
        }
        return false;
      }
    
      public static void main(String[] args) {
        int [][] testArray = new int [][]{
            {   0,   2,   1,   2,   0,   5,   5,   5 },
            {  21,  21,   7,   7,   7,  21,  21,  21 },
            {  21,  21,  21,  21,  21,  21,  21,  21 },
            {  21,  21,  23,  42,  41,  23,  21,  21 },
            {  60,  56,  57,  58,  53,  52,  47,  51 },
            {  61,  65,  70,  72,  73,  78,  82,  98 },
            { 112, 121, 112, 134, 123, 100,  98, 111 },
            { 136, 136, 136, 134, 147, 150, 154, 134 }
        };
        for (int[] row : testArray) {
          for (int val : row) {
            System.out.print(contains(testArray, val) + " ");
          }
          System.out.println();
    
        }
        System.out.println();
        System.out.println();
        final int[] notInMatrix = { -1, 3, 4, 6, 8, 22, 30, 59, 71, 113, 135 };
        for (int val : notInMatrix) {
          System.out.print(contains(testArray, val) + " ");
        }
        System.out.println();
      }
    }
    

    We can improve the acutal runtime by determining the candidate lines through a binary search algorithm so that candidate lines are found in O(log(n)) instead of O(n). The asymptotical runtime will still be O(n) for square matrices and O(log(n) + k) for non-square n x k matrices. The idea for this was taken from Saeed Bolhasani's answer.

      private static int findCandidateRow(final int[][] m, final int value) {
        int lower = 0;
        int upper = m.length;
        int middle = (upper + 1) / 2;
        while (middle != m.length 
            && middle != 1
            && (m[middle][0] < value || m[middle - 1][0] > value)) {
          if (m[middle][0] < value) {
            lower = middle;
          } else {
            upper = middle;
          }
          middle = lower + (upper - lower + 1) / 2;
        }
        return middle;
      }
    

提交回复
热议问题