修改矩阵并将列和行设置为零的算法

Posted

技术标签:

【中文标题】修改矩阵并将列和行设置为零的算法【英文标题】:Algorithm to modify a matrix and set columns and rows to zero 【发布时间】:2014-01-01 20:21:45 【问题描述】:

这在技术上是一个代码挑战。 我在一次采访中被问到一个有趣的问题,我希望能得到一些见解,因为我能想出的最佳答案是 O(2n^2) - n 平方类别,但仍然是蛮力的。

假设您有一个 M × N 大小的矩阵(数组数组 (int[][]))

1 2 4 3 1
0 5 3 7 7
5 8 9 2 8
6 7 0 8 9

如果单元格包含零,则将整行和整列设置为零。 制作结果:

0 2 0 3 1
0 0 0 0 0 
0 8 0 2 8
0 0 0 0 0 

最快和/或最好的方法是什么?


我自己的答案是迭代整个数组数组,跟踪行和列以清零,然后将它们清零。

public void zeroOut(int[][] myArray)
    ArrayList<Integer> rowsToZero = new....
    ArrayList<Integer> columnsToZero = new....

    for(int i=0; i<myArray.length; i++) // record which rows and columns will be zeroed
        for(int j=0; j<myArray[i].length; i++)
            if(myArray[i][j] == 0)
                if(!rowsToZero.contains(i)) rowsToZero.add(i);
                if(!columnsToZero.contains(j)) columnsToZero.add(j);
            
        
    
    for(int row : rows) // now zero the rows
        myArray[row] = int[myArray.length];
    

    for(int i=0; i<myArray.length; i++)
        for(int column: columns) // now zero the columns
            myArray[i][column] = 0;
            
    

有没有更好的算法?有没有更好的数据结构来表示这个矩阵?

【问题讨论】:

你有 O(N^2) 的数据要处理,所以显然它不能比 O(N^2) 更快地完成。坚持你所拥有的。 你必须访问每个单元格以检查是否存在 0,所以你不能做得比 O(m*n) 更好......你的方法对我来说似乎很直观 如何从每一行和每一列制作一个 HashMap(你最终得到 M + N HashMap(s)),然后对他们每个人询问:if (hmap.contains(0))/*zero out the column or row it represents*/ @RichardPlunkett M x N 矩阵(大写 N)。 M*N=n 要处理的数据数。 OP说O(2n^2),你说O(N^2) 您应该使用HashSet 而不是ArrayList,因为contains 方法的成本为O(1) 而不是O(n)。为了将行归零,您可以使用固定数组来降低内存成本。 【参考方案1】:

您可以通过取两个 int 来做到这一点,但唯一的条件是行数和列数应小于或等于 32。您可以对大于 32 执行相同操作,但您必须采用整数数组。

所以逻辑是:

取两个整数,即rowcol 遍历矩阵如果matrix[i][j] = 0而不是设置rowcol中的对应位 如果设置了rowcolumn 的对应位,则再次遍历并设置matrix[i][j] = 0

时间复杂度相同O(N^2),但内存效率高。请在下面找到我的代码。


检查array[row][col] == 0是否为0而不是设置rc中的对应位。

        int r = 0, c = 0;
        for (int row = 0; row < 5; row++) 
            for (int col = 0; col < 7; col++) 
                if (array[row][col] == 0) 
                    r = r | (1<<row);
                    c = c | (1<<col);
                
            
        

现在,如果设置了任何一个位,则将单元格设置为 0。

  for (int row = 0; row < 5; row++) 
        for (int col = 0; col <7; col++) 
            if (((c&(1<<col))!=0) || ((r&(1<<row))!=0)) 
                array[row][col] = 0;  
            
        
    

【讨论】:

这和我的实现差不多,但是没有代码,能解释一下设置位是什么意思吗?我不熟悉 @EliteOctagon 检查我的编辑。时间复杂度相同,但内存效率更高。【参考方案2】:

如何将矩阵拆分为相等的较小矩阵部分并计算行列式,以便您可以预测该矩阵部分内有一个零。 然后只对这个预选的矩阵使用蛮力机制来确定 在哪一行或哪一列中,零是。

行列式只是一个建议,也许您可​​以使用其他类型的线性代数 预测零值的算法和规则

更新:

如果您使用快速排序概念来组织每一行的临时。然后你只需要循环直到第一个非零元素出现。 您需要在排序过程中记住哪个列索引与 0 相关联

意思

 1 2 6 0 3

快速排序(

 0 1 2 4 6

当你记住列索引时,你现在直接知道哪一行用 0 填充,哪一列,

快速排序的平均值 O(n log n) Worstcase n * n

也许这已经提高了整体的复杂性。

【讨论】:

我对你的逻辑很感兴趣,你能写一些代码和/或解释一下吗? 不,不是今天。一开始我只是想快速排序。这来自两个方面,将问题分解为更小的和平。然后我想如何以正确的方式拆分它。所以我想到了一些代数函数。也许在星期天我可以给你一些解决方案。真正有趣的问题。 我想我会尝试到周日晚上。即使我到现在都没有解决方案,也许我可以提供一些灵感。我想我们会在这里再次见面。 我认为时间复杂度是 O(N^2)。因为您在这里使用的分区需要 O(N),而对于 N 行,它是 O(N^2)。所以这个问题最棘手的部分是你如何记住我认为最好的方法是位设置的行和列。 确实如此。但我不认为我的复杂性计算是正确的。【参考方案3】:

到目前为止,似乎没有人真正想出一种更快/更好的算法,所以这个似乎就是这样。谢谢大家的意见。

public void zeroOut(int[][] myArray)
    ArrayList<Integer> rowsToZero = new....
    ArrayList<Integer> columnsToZero = new....

    for(int i=0; i<myArray.length; i++) // record which rows and columns will be zeroed
        for(int j=0; j<myArray[i].length; i++)
            if(myArray[i][j] == 0)
                if(!rowsToZero.contains(i)) rowsToZero.add(i);
                if(!columnsToZero.contains(j)) columnsToZero.add(j);
            
        
    
    for(int row : rows) // now zero the rows
        myArray[row] = int[myArray.length];
    

    for(int i=0; i<myArray.length; i++)
        for(int column: columns) // now zero the columns
            myArray[i][column] = 0;
            
    

【讨论】:

这个解决方案抛出 java.lang.ArrayIndexOutOfBoundsException :) 请更正它。【参考方案4】:

我刚刚遇到了这个问题,并为此制定了解决方案。 我希望我能得到一些关于代码更好/更差以及运行时的反馈。 这一切都很新:)

public static void zeroMatrix(int[][] arr1)


    ArrayList<Integer> coord = new ArrayList<>();

    int row = arr1.length; 
    int column = arr1[0].length;
    for(int i=0; i < row; i++)
    
        for(int j=0; j < column; j++)
        
            if(arr1[i][j]==0)
             
                coord.add((10*i) + j);
                   
        
    

    for(int n : coord)
    
         int j=n%10;
         int i=n/10; int k=0;
        int l=0;
        while(k<row)
        
            arr1[k][j]=0;
            k++;
        
        while(l<column)
        
            arr1[i][l]=0;
            l++;
        
    

【讨论】:

【参考方案5】:

我使用过 HashMap。看看这是否有任何帮助

import java.util.*;
class ZeroMatrix

public static void main(String args[])

int mat[][]=new int[][]0,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,0;
HashMap<Integer,Integer> ht=new HashMap<Integer,Integer>();
for(int i=0;i<5;i++)

for(int j=0;j<4;j++)

if(mat[i][j]==0)
ht.put(i,j);



//Set the Respected Rows and colums to Zeros 
Set set=ht.entrySet();
Iterator itr=set.iterator();
while(itr.hasNext())

Map.Entry m=(Map.Entry)itr.next();
int i=(Integer)m.getKey();
int k=(Integer)m.getValue();
for(int j=0;j<4;j++)

mat[i][j]=0;

for(int j=0;j<5;j++)

mat[j][k]=0;



//Printing the Resultant Zero Matrix 

for(int i=0;i<5;i++)

for(int j=0;j<4;j++)

 System.out.print(mat[i][j]);

System.out.println();



【讨论】:

以上是关于修改矩阵并将列和行设置为零的算法的主要内容,如果未能解决你的问题,请参考以下文章

使用 SSE 和 AVX 查找矩阵中的最大元素及其列和行索引

如何识别两个数据矩阵之间的哪些列和行匹配?

scipy稀疏矩阵:删除所有元素为零的行

在R中同时移动矩阵的特定列和行的有效方法

和为零的子矩阵

matlab找出两个向量都不为零的元素,并找出元素的位置