找到表示行方式排序矩阵中最小整数的行

时间:2010-11-29 12:45:19

标签: arrays algorithm language-agnostic matrix

最近在Java电话采访中我被问到这个问题:

您将获得一个NxN二进制(0-1)矩阵,其中包含以下属性:

  • 对每一行进行排序(0的序列,后跟1的序列)
  • 每一行代表一个无符号整数(通过读取位)
  • 每一行都是唯一的

示例:

0 1 1
1 1 1
0 0 1

对每行中的位值进行排序,行代表整数3,7和1。

找到表示最小整数的行。在上面的示例中,答案是第3行,它表示整数1。

我从二次复杂的蛮力开始。采访者回答说我没有利用分类财产。

经过深思熟虑之后,我在每一行都使用了二元搜索,然后来到了O(nlogn)。他问我是否可以进一步改进。我想了很多但没有改进。

如果有人能提出任何有关推动它的指示,我将不胜感激。

另一个例子:

0 1 1 1
0 0 0 1
0 0 0 0
1 1 1 1

答案是第3行,代表整数0。

14 个答案:

答案 0 :(得分:101)

从第1行开始。向右走直至找到第一个1。然后转到第2行,但保持在同一列中并重复向右行进直到达到1。反复这样做。你上次走右路的那一行是你的答案。

这是一个O(N + M)解(对于NxM矩阵,或O(N)对于问题中给出的方形NxN矩阵)。

使用你的例子:

0 1 1 1
0 0 0 1
0 0 0 0
1 1 1 1

这里的.代表遍历的路径:

. . 1 1
0 . . .
0 0 0 . . Last right step, therefore this is our answer
1 1 1 1 .

此解决方案适用于非方矩阵,保留了NxM矩阵的最坏情况O(N + M)效率。

为什么这样做?保证数字将被排序意味着每一行将是一系列的0,后跟一系列的1。因此,行的大小相当于在达到1之前你可以走多远。因此,如果一行只能跟随0来进一步,那么它必须比我们以前处理的任何东西都长。

Python代码:

li = [[0, 1, 1, 1],
      [0, 0, 0, 1],
      [0, 0, 0, 0],
      [1, 1, 1, 1]]

ans, j = 0, 0
for i, row in enumerate(li):
  while j < len(row) and row[j] == 0:
    j += 1
    ans = i

print "Row", ans+1, "".join(map(str, li[ans]))

还有一个更简单的解决方案,因为总是有一个正方形NxN矩阵和不同的行在一起的约束。它们一起表示具有最低值的行将是0 0 ... 0 10 0 ... 0 0。这是因为在矩阵中有N + 1个可能的数字,因此“缺失”数字为0(在这种情况下,表示的最小值为1)或者其他东西(最小值为0)。

有了这些知识,我们检查右边的第二列是否为0.当我们找到一个时,我们向右看,如果它包含另一个0,我们得到了答案(只有一行以{结尾) {1}})。否则,我们继续在列中搜索另一个0.如果我们找不到另一个0,我们找到的第一个是我们要查找的行(只有一行以0结尾,因为在01中没有结尾,这是最小的。)

Python代码:

00

该解决方案在O(N)中以较少的难度回答了该问题,但是将其概括为处理非方形NxM矩阵或非不同数字将使其最坏情况效率O(N ^ 2)。我个人更喜欢第一种解决方案。

答案 1 :(得分:50)

最小的数字必须是0或1.(因为没有重复并且行被排序)。所有你需要做的就是回到最后一列,如果ti包含0,则最低数字为0,否则最低数字为1.

编辑 - 解释
在您拥有约束的N行中,最多可以有N+1个唯一值 所以肯定至少0或1必须在矩阵中....

编辑2 - 算法

//assuming the matrix is 0 indexed base
for i = 0...N-1
  if M[i][N-1] == 0
    return "Value is 0 in row i";
for i = 0...N-1
  if M[i][N-2] == 0
    return "Value is 1 in row i";
//because of the explanation above the flow will never reach here.

答案 2 :(得分:41)

由于数字是唯一的并且由于数字是排序的,因此很明显,对于N的任何值,最小数字可以是[0(N-1次)后跟1]或0( N次)。

  

例如,对于N = 4,最小的数字   可以是0001或0000.

换句话说,我们希望找到的数字的倒数第二位为0.而最后一位数字可以是0或1

然后,这个问题简化为在数组中找到这些模式,这可以使用简单的for循环

来完成
int rowNum = -1;

for(int i=0;i<N;i++)
{
    if(arr[i][N-2]==0) //Second last digit is 0. Hence the number could be min.
    {
        rowNum = i;

        if(arr[i][N-1]==1) // If number of the form 0001 was found, keep looking for 0000
        {
            continue;
        }
        else
         //If number of the form 0000 was found, exit. 
         //No other number can be lesser than 0000
        {
            break;
        }
    }
}
return rowNum;

此算法的复杂度为 O(n)

答案 3 :(得分:24)

您想要找到最大零数的行。

  • arr[0][0]
  • 开始
  • 如果是0,请检查元素 它的权利,arr[0][1]
  • 如果不是0则跳过该行 开始检查中的元素 当前元素下面的下一行。

继续操作,直到你越过最后一行/最后一列,或者找到一个全零的行。

<强>算法:

i = 0 
j = 0 
answer = 0 

# continue till i is a valid index.
while(i<N) 

        # continue till j is valid index and ele is 0.
        while(j < N AND arr[i][j] == 0)

                # move towards right.
                j++ 

                #update answer.
                answer = i 

                # found a row with all zeros.
                if(j == N)  
                        break all loops.
                end-if

        end-while

        # skip current row..continue on next row.    
        i++ 

end-while

print answer

复杂性为O(N+N) O(N),即线性。

Java implementation

相关问题哪个使用完全相同的技巧

How to efficiently search in an ordered matrix?

答案 4 :(得分:3)

由于每行中的位都已排序,因此一旦找到1位,右侧的所有位也必须为1。换句话说,该数组仅存储形式为2 ^ n-1的值。

所以答案是条目最多的行是最小的。

然而,由于只能存在2 ** m-1个条目,并且有n个条目,并且没有两个相同,我们可以推导出更多 - 对于任何N,有N + 1个这样的值。所以要么必须存在0或1,因为我们知道没有重复。

所以寻找一个空行(只有一行最右边的列为零)。如果找不到,答案是1,否则为0。

O(N)

答案 5 :(得分:3)

Start at the top-left.

The first row is the best row so far.

Repeat until you reach the bottom:
  If you're not already over a 1:
    Go right until you find a 1.
    This row is the best row so far.
  Go down one row.

Report the best row that was found.

你永远不会上升或离开 - 你只会下降(n-1)次并且不会超过(n-1)次,这就是O(n)。这通过实现你永远不必向左检查1来利用排序 - 如果在左边某处有1,那么当前点中也有1(因此该行中的数字至少是与上一行中的一样大。)

答案 6 :(得分:2)

如何以相反的顺序循环每一行并检查1s结束和零开始的位置?

事实上,保证在NxN中,最坏的情况是0不存在。所以你可以检查每一行的最后两个条目。这使它成为线性的。

由于我的解释没有被理解,这里有点伪代码:

int lowestRow = -1;
for (k = 0; k < array.length; k ++) {
   byte[] row = array[k];
   if (row[row.length - 1] == 0) {
       lowestRow = k;
       break;
   }
   if (row[row.length - 2] == 0) {
       lowestRow = k;
       //possibly look for all-zeroes, so not breaking
   }
}

答案 7 :(得分:1)

你必须从最后一列开始,检查元素的总和是否为N-1,一旦你找到了一个sum = N-1的列搜索包含0的列,这就是你的那个正在寻找...

答案 8 :(得分:1)

@codaddict的优化版本

int best = N;
int answer = -1;

for(int i=0;i<N;i++)
   for(int j=0;j<best;j++) 
       if (arr[i][j] != 0) {
          best = j;
          answer = i;
        }

一旦确定此行不会比当前答案更好,内部循环就会停止。这可能会减少很多搜索到目前为止比最佳答案更长的行。

答案 9 :(得分:1)

查找哪一行在最远列中具有第一个非零值。如果它是二进制的,左边是MSB,右边是LSB,那么答案就是以最多的零开头的行。

答案 10 :(得分:1)

如果可以的话,我会将此作为对Jeremy答案的评论,因为他的解决方案大多是正确的。另外,我喜欢这种方法。在许多情况下,它将比其他答案快得多。可能存在问题。如果“每行都已排序”。并不意味着所有人都向右移动,但有其他含义(我可以想到一些含义。我需要更多来自个人提问)。一个问题......如何关于0011和0010.行排序可能意味着您正在实施的算法已经被使用。他的答案中指定的算法无法区分这两者。我会将两个答案的索引存储在一个数组中。如果数组长度是1那么你有一个解决方案,否则你需要进一步递归...只是一个想法。如果有人读到这篇文章可以在其他帖子上发表评论,请在评论中将其引用到他的帖子中。这是一个严重的问题,如果技术上不正确的答案得到检查,那将是令人沮丧的。如果我的评论被添加,我将完全删除我的答案。

答案 11 :(得分:1)

最小的数字可以是0,看起来像(0000 ... 0000)或1看起来像(0000 ... 0001)。

每个较大的数字看起来像(xxxx ... xx11)。所以你应该检查每一行的倒数。如果为0,则检查最后一位是否为0.如果是,则为最小数字。如果没有,那么记住行号并继续查找倒数第二位的0行。如果你找到它,这将是最小的数字。如果没有,则找到的第一个数字最小。

这是N + 1步骤(最坏情况)的解决方案,即O(N)复杂度。

答案 12 :(得分:0)

我不知道它是否被录取,但是如果它被排序,你不需要只将每行转换为十进制数并选择具有较低行的行。 例如:

[0111] -> 7
[0011] -> 3
[0000] -> 0
[0001] -> 1

解决方案是值为0的行。或?

答案 13 :(得分:0)

我写了一个O(n)算法,类似于上面所说的,我们从左上角开始向下工作:

a = [
        [0, 1, 1, 1],
        [0, 0, 0, 1],
        [0, 0, 0, 0],
        [1, 1, 1, 1]
    ]
a2 = [
        [0, 0, 0, 0],
        [0, 1, 1, 1],
        [0, 0, 0, 1],
        [1, 1, 1, 1]
    ]

def search(a):
    n = len(a)
    c = 0
    r = 0
    best_row = 0
    while c<n and r<n:
        if a[r][c] == 0:
            c += 1
        else:
            best_row = r
            r += 1

    if c==n : best_row = r
    print( " best row: %d" % best_row )

search( a )
search( a2 )