【问题标题】:searching in 2d array as O(n) with unsorted rows在二维数组中搜索为 O(n) 且未排序的行
【发布时间】:2018-11-11 22:00:35
【问题描述】:

我需要编写一个方法,它采用二维数组'int [][] m'和一个值'val'并检查val是否在数组中复杂度为O(n),而n定义为行数和 m 必须是平方的

可以用作我的方法的参数的数组必须为此方法返回true:

(如果返回 true 则数组符合要求)

public static boolean test(int[][] m) {
    int n = m.length;
    for (int r = 0; r < (n - 1); r++)
        for (int c = 0; c < n; c++)
            for (int i = 0; i < n; i++)
                if (m[r][c] > m[r + 1][i]) return false;
    return true;
}

此数组返回 TRUE:

int [][] arr3 = 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,},
};

如果val 在数组中并且看起来像这样,我的方法应该返回true:

public boolean findValTest(int [][] m, int val){...}

【问题讨论】:

  • n 是如何定义的?
  • 哦,抱歉,添加了 :) @Turing85
  • ... 和 O(n) 应该是最坏情况下的执行时间?如果是这样,我感觉这是不可能的。
  • 你的val在哪里?
  • 乍一看,每一列都是排序的,所以每列可以得到 O(log n),然后将其乘以列数得到 O(n log n)。但就获得 O(n) 而言?我有兴趣阅读其他人的想法

标签: java arrays search time-complexity


【解决方案1】:

这是可能的。矩阵m 是一个大小为 n x n 的方阵。核心思想灵感来自oleg.cherednik's answer。一旦我们在m 中找到row,即m[row][0] &gt;= val,我们就知道val 必须在rowrow - 1 的任一行中(因为row - 1 上的相同比较是@987654331 @)。因此,我们必须找到我们的候选行(O(n)),然后只分析这两行(也是O(n))。如果m不是正方形,而是矩形,算法复杂度为O(n + k),其中n是行数,k m 中的列数。这导致了以下算法。

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();
  }
}

我们可以通过二分查找算法确定候选行来提高实际运行时间,以便在O(log(n))中找到候选行,而不是O(n)。方阵的渐近运行时间仍为 O(n),非方阵 nxk 矩阵的渐近运行时间仍为 O(log(n) + k) .这个想法来自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;
  }

【讨论】:

  • 我能问一下为什么你每次都增加 ++row 而不是 row++ 吗?
  • @MatanCohen 这是个人风格的事情。对我来说++i 是“更明显的操作”(先递增,返回递增值)。因此,我只在必要时使用前置增量作为默认值和后置增量。
  • 我可以向投反对票的人询问导致投反对票的原因吗?
【解决方案2】:

您的解决方案就在这里。我做了一个对第一列进行二进制搜索的函数。如果 val 在第一列中找到,则函数返回 true,否则最后一段 'l' 和 'r' 对我们有利。 'r' 和 'l' 总是等于只有一个距离(r=l 或 abs(r-l)=1 )。 'r' 和 'l' 的下限是 val 可能存在于其中的预期行。所以我们应该搜索这一行。
二分查找的 O(n) 为 Log(n),行查找的 O(n) 为 n。所以最终的 O(n) 将是 n.code 在这里:

static boolean binarySearch(int arr[][], int l, int r, int x)
{
    if (r>=l)
    {
        int mid = l + (r - l)/2;

        // If the element is present at the 
        // middle itself
        if (arr[mid][0] == x)
           return true;

        // If element is smaller than mid, then 
        // it can only be present in left subarray
        if (arr[mid][0] > x)
           return binarySearch(arr, l, mid-1, x);

        // Else the element can only be present
        // in right subarray
        return binarySearch(arr, mid+1, r, x);
    }

    // We reach here when element is not present
    //  in array

    int row = Math.min(l,r);
    for(int i=0; i<arr[0].length ;i++)
      if(arr[row][i]==x)
        return true;
    return false;
}

【讨论】:

    【解决方案3】:

    像那样。如果 i 行上的每个数字等于或小于i+1 行上的每个数字,您可以只检查每行中的第一个元素来定义一行,其中所需的值可能是.只有全扫描才能找到未排序行中的元素。

    此算法必须扫描 2 个完整的行,即 O(n) 其中 n - 行数

    public static boolean findValTest(int[][] m, int val) {
        for (int row = 0; row < m.length; row++) {
            if (m[row][0] <= val && row != m.length - 1)
                continue;
    
            int r = row;
    
            while (r >= row - 1 && r >= 0) {
                for (int col = 0; col < m[r].length; col++)
                    if (m[r][col] == val)
                        return true;
    
                r--;
            }
    
            return false;
        }
    
        return false;
    }
    

    测试用例:

    System.out.println(findValTest(arr3, -1)); // false
    System.out.println(findValTest(arr3, 5)); // true
    System.out.println(findValTest(arr3, 7)); // true
    System.out.println(findValTest(arr3, 55)); // false
    System.out.println(findValTest(arr3, 47)); // true
    System.out.println(findValTest(arr3, 147)); // true
    System.out.println(findValTest(arr3, 200)); // false
    System.out.println(findValTest(new int[][] { { 3, 4, 5 } }, 4));   // true
    

    【讨论】:

    • 不幸的是行未排序
    • 是的,但是Every number at row 'i' is equals or smaller then every number on row 'i+1
    • 对于val=4, {{3,4,5}} 这个最小场景,该算法也没有返回正确的值
    • @oleg.cherednik 实际上效果不佳.. 试图找到 7 并在我的测试仪上返回 21..
    猜你喜欢
    • 2019-02-01
    • 1970-01-01
    • 2012-04-16
    • 1970-01-01
    • 2011-08-26
    • 1970-01-01
    • 1970-01-01
    • 2019-07-01
    相关资源
    最近更新 更多