在O(log n)中的排序矩阵(行n列)中查找数字

时间:2012-05-14 19:41:12

标签: performance algorithm

假设我有一个矩阵(MxN),其行和列已排序。

  1. 每行中的所有元素按升序排列
  2. 每列中的所有元素按升序排列
  3. 所有元素都是整数
  4. 无法做出其他假设

    示例:

    [1 5 8 20]

    [2 9 19 21]

    [12 15 25 30]

  5. 我必须找到矩阵中是否存在给定数字(基本搜索)。我有一个运行O(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++;
      } 
    }
    

    但我被问到O(log (MxN)) == O(Log(n))解决方案。任何想法??

5 个答案:

答案 0 :(得分:74)

O(log(M * N))解决方案不可用于此任务。

让我们看一个简化的任务:在“排序”方形矩阵中假设次要对角线(绿色)之上的所有元素小于给定数量,次要对角线(红色)之下的所有元素都大于给定数量,并且没有对元素的额外假设次要对角线(黄色)。

enter image description here

这项任务的原始假设和这些额外的假设都没有告诉我们次要对角线上的元素是如何相互关联的。这意味着我们只有一个N个整数的未排序数组。我们无法在未排序的数组中找到比O(N)更快的给定数字。因此,对于方形矩阵的原始(更复杂)问题,我们无法获得比O(N)更好的解决方案。

对于矩形矩阵,拉伸方形图片并相应地设置其他假设。这里我们有min(N,M)个排序的子阵列,每个子阵列的大小为max(N,M)/ min(N,M)。这里搜索的最佳方法是使用线性搜索来查找可能包含给定值的一个或多个子数组,然后在这些子数组中使用二进制搜索。在最坏的情况下,有必要在每个子阵列中进行二进制搜索。复数是O(min(N,M)*(1 + log(max(N,M)/ min(N,M))))。因此,对于矩形矩阵的原始(更复杂)问题,我们无法得到比O更好的解(min(N,M)*(1 + log(max(N,M)) - log(min(N,M))) )。

答案 1 :(得分:6)

不可能比O(n)做得更好。有些人(本页至少有三个人)认为他们可以做得更好,但那是因为他们的算法错误,或者因为他们不知道如何计算算法的复杂性所以他们试图猜测它。 This blog post非常好,并会向你解释这些人的错误。

O(n)最优的证明草案:考虑以下矩阵:

1     2     3     4     5     6 … (n-2)  (n-1) (n+1)
2     3     4     5     6     7 … (n-1)  (n+1) (n+2)
3     4     5     6     7     8 … (n+1)  (n+2) (n+3)
…     …     …     …     …     … … …      …     …
(n-2) (n-1) …     …     …     … … …      …     (2n-1)
(n-1) (n+1) …     …     …     … … …      …     2n
(n+1) (n+2) …     …     …     … … (2n-1) 2n    (2n+1)

如果您在此矩阵中查找n,则必须至少检查一行,如果n位于行中,因为n可能位于任何行中。 (证据不完整,但这是想法)

答案 2 :(得分:4)

您必须使用递归来解决此问题。 给定矩阵X和数字y,您可以在X的中间行上对y进行二分搜索,并将矩阵分成四个部分,例如:

A|B
---
C|D

A中的所有元素都小于y,D中的所有元素都大于y,y可以是B和C.迭代地在B和C中找到y。

由于高度(A)=高度(B)\约=高度(C)=高度(D),尺寸(X)> = 2 *(尺寸(B)+尺寸(C))。因此,如果O(logn),则产生复杂性。

def find(X,y):
    a,b = X.shape
    i = a /2
    j = binsearch(X[i,:], y)
    if X[i,j]==y:
        return True
    else:
        return find( X[ (i+1):a, 0:(j-1)], y ) or find( X[ 0:i, j:b], y )

答案 3 :(得分:2)

由于行和列都是排序的,如果我们查看每行的第一个元素,我们可以找到哪一个包含我们正在寻找的数字。然后,我们再次利用每一行中的元素进行排序并找到该数字这一事实 我所知道的最快搜索算法是二进制搜索,其复杂度为O(log n),因此总复杂度为O(log m + log n)。 这是一个例子,假设我们正在寻找28:

 1  2  3  4  5  6  7  8  9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
  • 我们对第一列(1,11,21,31,41)的元素进行二分搜索,发现该行是第三列,因为它的第一个元素小于我们的数字但是下一行的第一个元素更大。 步数:2 (21,31,找到)
  • 我们在第三行(21,22,23,24,25,26,27,28,29,30)再次进行二分搜索并找到我们的号码。 步数:2 - 3 (25,27或28,找到)

答案 4 :(得分:0)

我认为这可以在O(log(n * n)* log(n))时间内完成,其中n是no。方阵的行。

通过Matrix的属性,矩阵的主对角线是一个有序数组。因此,我们可以在O(log(n))中搜索元素或其下界。现在,使用这个元素作为枢轴,我们有4个子矩阵。我们可以说子矩阵(左上角)中的所有元素都较小,子矩阵(右下角)中的所有元素都较大。因此,我们可以从搜索空间中删除它。

现在,递归搜索子矩阵(右上角)和子矩阵(左下角)。

因为,在每一步,我们执行log(n)搜索(沿主要对角线)广告,最多可以有log(n * n)个步骤(因为我们在每个步骤中将搜索空间减少了一半)。

因此,时间复杂度= O(log(n) log(n n))。

如果有任何问题,请更正。

参考 - [书]破解编码面试(问题11.6)