【问题标题】:Binary Search in 2D Array二维数组中的二分搜索
【发布时间】:2010-12-12 11:55:08
【问题描述】:

我想知道,二分搜索可以应用于二维数组吗?

  • 阵列上的条件是什么?二维排序??
  • 复杂度需要多长时间?
  • 算法将如何改变搜索边界 (minX,maxX,minY,maxY)?

编辑:

一维二分搜索维护两个指针minXmaxX.. 它选择中间索引(minX+maxX)/2并将其与搜索值进行比较,如果大于则更改maxX,否则更改minX...直到minX>=maxX

普通二进制seacrh的伪代码:

 min := 1;
  max := N; {array size: var A : array [1..N] of integer}
  repeat
    mid := min + (max - min) div 2;
    if x > A[mid] then
      min := mid + 1
    else 
      max := mid - 1;
  until (A[mid] = x) or (min > max);

谢谢

【问题讨论】:

  • 您有什么特别想要实现的目标吗?
  • 我的第一个建议是让你的一维算法得到解决。可能存在一些边界问题。考虑一个大小为 1 的数组。这里是 min = 1max = 1。然后mid := min + (max - min) div 2 产生mid = 0(假设整数运算)。那么谁知道A[mid] 等于什么(假设A 被索引为:1..N)。
  • @NealB 这只是维基百科中的代码......没关系它只是为了演示......
  • 再看一眼...我的错误:mid := min + (max - min) DIV 2 结果为 1,这很好。
  • 在我的例子中,我有一个 X=压力和 Y=温度的二维点列表。给定一个输入 targetKey (X,Y),我需要在数据集中找到最接近 targetKey 的那个。起初我以为我可以使用 BinarySearch,在类型上实现一个自定义比较器,比较两点之间的距离。但是这不起作用,因为在运行每个二分搜索之前,它需要按照与 targetKey 的距离顺序对列表进行重新排序。更好的解决方案是使用四叉树。仅供参考,这里有一个 C# 实现:link

标签: algorithm multidimensional-array binary-search


【解决方案1】:

我以O(m + n) 时间复杂度的简单方式解决了它,其中 m = 否。行数,n = 否。列数

算法很简单:我从右上角开始(我们也可以从左下角开始),如果当前元素向左移动 大于要搜索的值,如果当前元素则为底部 小于要搜索的值。

java代码如下:

public static int[] linearSearch(int[][] a, int value) {
    int i = 0, j = a[0].length - 1; // start from top right corner

    while (i < a.length && j >= 0) {
        if (a[i][j] == value) {
            return new int[]{i, j};
        } else if (a[i][j] > value) {
            j--; // move left
        } else {
            i++; // move down
        }
    }
    // element not found
    return new int[]{-1, -1};

}

Gist

您可以使用称为Improved Binary Partition 的方法进一步降低时间复杂度。

【讨论】:

    【解决方案2】:

    我去年就考虑过这个问题...所以,我选择了这种方法:

    考虑您的二维数组表示平面中的点。例如,您的元素 A[i][j] 表示 x = i 和 y = j 的点。要在平面上使用二分搜索,我使用以下条件对所有点进行排序:

    点 p1

    • (p1 的 x 坐标)
    • (p1 的 x 坐标)=(p2 的 x 坐标)和(p1 的 y 坐标)

    否则 p1 >= p2。

    现在,如果我们查看二维数组,第二行的元素应该大于第一行的元素。在同一行中的元素照常排序(根据它们的列号)。

    换句话说:

    • A[i][j] > A[k][j] 当且仅当 (i>k)。 (在不同的行和同一列中)
    • A[i][j] > A[i][k] 当且仅当 (j>k)。 (在同一行不同列)

    假设您的数组有 N 行和 M 列。现在你应该(暂时)使用这个公式(T - 临时数组)将你的二维数组转换为一维数组:

    for i:=0 to N-1 do
        for j:=0 to M-1 do
            T[i*N + j]:= A[i][j];
    

    现在你有了一维数组。以通常的方式对其进行排序。现在您可以使用简单的二进制搜索算法在其中进行搜索。

    或者您可以使用以下公式将排序后的数组转换回二维数组:

    for i:=0 to N*M-1 do
        A[i div N][i - (i div N)*N]:= T[i];
    

    并使用两个二分搜索:

    一个按 x 坐标(按我们的意思按行)搜索,另一个按 y 坐标(按我们的意思按列)搜索同一行中的元素。

    换句话说,当您计算 mid = mid + (max - min) div 2 时,您可以将元素 A[mid][0] 与您的关键元素(在您的代码中它具有 x 名称)进行比较,当您找到行与您的元素,您可以在该行中调用另一个二进制搜索(A[mid] 中的二进制搜索)。

    两种方法的复杂性:

    • 用于转换数组中的简单二分搜索:log(N*M)
    • 用于二维数组中的两个二分搜索:log(N) 用于外部搜索(以行为单位)+ log(M) 用于内部搜索(以列为单位)。

    利用对数函数的性质,我们可以简化最后一个表达式:log(N) + log(M) = log(N*M)

    因此,我们证明了这两种方法具有相同的复杂性,并且使用哪一种并不重要。

    但是,如果对你来说不难,我建议你简单地将数组转换为一维并使用简单的二进制搜索(它非常简单且易于调试和检查)。

    【讨论】:

      【解决方案3】:

      二分搜索以分而治之的方式工作,

      int r = arr.length; // ROW Count
      int c = arr[0].length; // Column Count
      int start = 0; // Initialize with the 0
      int end = r*c-1; // Last Index
      

      我们将继续迭代while循环,每次我们根据要求更新开始和结束索引..
      而(开始

      int mid = (start+end)/2;
      int midX = mid/c;
      int midY = mid%c;
      

      如果当前值等于搜索元素,那么我们只需打印并返回它。

      if(arr[midX][midY] == searchElement){
      return true;
      }
      

      如果当前值小于搜索元素,那么我们只需将中间值更新为 mid = mid + 1

      if(arr[midX][midY] < searchElement){
      start = mid+1;
      }
      

      如果当前值大于搜索元素,那么我们只需将中间值更新为 mid = mid - 1

      else{
      end = mid-1;
      }
      

      【讨论】:

      • arr = [[1,4],[2,5]], target = 2 这种情况对于上述算法失败
      【解决方案4】:

      二分搜索需要对数组进行排序。反过来,排序需要数组元素的总排序关系。在一维中很容易理解这意味着什么。我认为您必须在二维数组中定义一个一维索引,并确保数组元素沿该索引排序。

      您有多种一维索引方案可供选择,基本上任何空间填充曲线都可以。最明显的想到的是:

      • 从第一个元素开始,沿着每一行读取,在每一行的末尾转到下一行的第一个元素。
      • 相同,逐列替换。
      • 对角化,您可以依次阅读每条对角线。

      就像@Bart Kiers,我不明白你的第二点。

      【讨论】:

      • 我看了修改后的问题。现在我不明白的是第三而不是第二。
      【解决方案5】:

      假设它是一个一维数组,并在分而治之时计算正确的行和列:

      /**
      * @param grid {[[number]]} A 2D NxM grid of numbers
      * @param targetValue {number} The target value to search
      * @return {[number]} A list containing the row and column. For example, [0,5] means row 0 column 5 
      */
      function search (grid, targetValue) {
          let rows = grid.length;
          let cols = grid[0].length;
      
          let leftBound = 0;
          let rightBound = rows * cols - 1;
      
          while (true) {
              let currentIndex = parseInt((leftBound + rightBound) / 2);
              let currentRow = parseInt(currentIndex / cols);
              let currentColumn = currentIndex % cols;
              let currentValue = grid[currentRow][currentColumn];
      
              if (currentValue === targetValue) {
                  return [currentRow, currentColumn];
              }
              else if (rightBound <= leftBound) {
                  return [-1, -1];
              }
              else if (currentValue < targetValue) {
                  leftBound = currentIndex + 1;
              }
              else {
                  rightBound = currentIndex - 1;
              }
          }
      
      }
      
      search([[11,12,15,23],[25,28,31,32],[35,45,47,47],[50,51,55,56],[65,65,78,88]], 45);
      

      【讨论】:

        【解决方案6】:

        不,不能对二维数组应用二分查找。

        二分查找的要求:

        要求1:物品已排序

        在一维数组中,这意味着什么很清楚。 但这对于二维数组到底意味着什么?

        要求 2:2 个方向

        二分搜索要求当你在其中选择一个项目时,你可以从那里进入两个方向。

        由于排序,每当您选择一个项目时,该项目都会提供足够的信息来了解您需要在 2 个方向中的哪个方向继续搜索。这允许将搜索范围分成两部分,这就是我们称之为二进制的原因。

        如果您在 2D 数组中选择一个项目,则有 4 个可能的方向(甚至更多:您也可以沿对角线移动)。即使所有项目都以某种方式排序,一个项目中的信息也无法告诉您必须前进的方向以及如何根据该方向拆分数组。

        只有当您可以将二维数组转换为已排序的一维数组时,才可能进行二分查找。 如果可以定义一个函数,将索引 x 和 y 组合成索引 i 用于已排序的虚拟 1D 数组,其中包含 2D 数组中的所有项目,并且 x 和 y 可以从 i 计算回来,那么您可以使用二进制搜索虚拟一维数组。该功能取决于二维数组中的项目如何排序。但这意味着您正在对一维数组而不是二维数组进行二进制搜索!

        【讨论】:

          【解决方案7】:

          这是二进制搜索解决方案,适用于所有测试用例。

          bool searchMatrix(vector<vector<int>>& matrix, int target) {
              int h = 0, w = matrix[0].size(), x;
              while(h < matrix.size() && w && (x = matrix[h][w-1]) != target) {
                  if (x > target) w--;
                  else h++;
              }
              return x == target;
          }
          

          【讨论】:

            【解决方案8】:

            您可以将二维数组转换为一维数组并在此处进行二进制搜索。 mxn 数组的复杂度为 O(log(m * n))

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2018-06-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-06-26
              • 2014-06-04
              相关资源
              最近更新 更多