如果该行或列包含0,则将矩阵中的每个单元格设置为0


152

给定一个0x和1s的NxN矩阵。将包含a的每一行设置0为all 0,并将包含a的每一列设置0为all 0

例如

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

结果是

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

一位微软工程师告诉我,有一种解决方案不涉及额外的内存,仅涉及两个布尔变量和一次传递,因此我正在寻找该答案。

顺便说一句,假设它是一个位矩阵,因此矩阵中只能包含1和0。


1
??什么是“无论何时遇到”?您以什么顺序遇到矩阵中的元素?而且,如果遇到所有位,反正不会得到全0吗?
ShreevatsaR

好吧,您决定遇到这些元素的顺序是您的决定,实际上,您只能将适当的元素设置为0s。如果遇到所有设置为0的位,则是的,矩阵仍将填充零。
jaircazarin-old-account

什么是“适当的要素”?是否给了两个矩阵,一个是“源”矩阵,一个是“目标”矩阵,并且必须决定以哪种顺序“相遇”元素以获得“目标”矩阵?
ShreevatsaR

1
我认为您对“ 1次通过”的想法听错了。它可以在2遍中线性完成,但是没有额外的内存,只有2个布尔值;-)所以我强烈认为这是他的意思(见下文)
Piotr Lesnicki

1
如果问题描述确实正确,可以请您与朋友仔细检查吗?我以为我可以用汉明码或奇偶校验位来做到这一点,但是到目前为止,我还没有成功,问题一直困扰着我。:)
csl

Answers:


96

好的,我在这里是凌晨3点,因此感到很累,但是我第一次尝试对矩阵中的每个数字进行精确2次遍历,所以在O(NxN)中,矩阵的大小是线性的。

我使用1rst列和第一行作为标记来知道哪里只有1的行/列。然后,有2个变量l和c可以记住第一个行/列是否也全为1。因此,第一遍设置标记并将其余的重置为0。

第二遍在行和列标记为1的位置设置1,并根据l和c重置第一行/列。

我强烈怀疑我能否一口气完成,因为开始时的方块取决于结束时的方块。也许我的第二次通过可以变得更有效率...

import pprint

m = [[1, 0, 1, 1, 0],
     [0, 1, 1, 1, 0],
     [1, 1, 1, 1, 1],
     [1, 0, 1, 1, 1],
     [1, 1, 1, 1, 1]]



N = len(m)

### pass 1

# 1 rst line/column
c = 1
for i in range(N):
    c &= m[i][0]

l = 1
for i in range(1,N):
    l &= m[0][i]


# other line/cols
# use line1, col1 to keep only those with 1
for i in range(1,N):
    for j in range(1,N):
        if m[i][j] == 0:
            m[0][j] = 0
            m[i][0] = 0
        else:
            m[i][j] = 0

### pass 2

# if line1 and col1 are ones: it is 1
for i in range(1,N):
    for j in range(1,N):
        if m[i][0] & m[0][j]:
            m[i][j] = 1

# 1rst row and col: reset if 0
if l == 0:
    for i in range(N):
        m [i][0] = 0

if c == 0:
    for j in range(1,N):
        m [0][j] = 0


pprint.pprint(m)

这里的一个问题是,如果n> sizeof(c)则会崩溃。为了将其扩展为在n的一般情况下工作,您需要动态调整位字段的大小,我认为这会违反问题给出的约束。
亚当

不,c不是位域,只是个布尔值。&=不是按位运算(好吧,但是是一个1位值),它在那里是因为c告诉您第一列是全1(真)还是包含0(假)。
史蒂夫·杰索普

2
如果第一行是[0,1,1,1 ...],它将失败。我的错误修复是将l初始化为m [0] [0]而不是1
paperhorse

实际上,对于范围(1,N)中的i,l = 1:l&= m [0] [i]对于范围(N)中的i:l&= m [0] [i]应该为l = 1
克里斯托夫·尼尔朗克

1
顺便说一句,我认为第二遍的条件应该是这样的:if m [i] [0] | m [0] [j]:
jaircazarin-old-account

16

这不可能一次完成,因为单个位会以任何顺序影响其前后的位。IOW无论您遍历数组的顺序如何,以后都可能会遇到0,这意味着您必须返回并将前一个1更改为0。

更新资料

人们似乎认为通过将N限制为某个固定值(例如8)可以解决这一问题。好吧,这是a)遗漏了要点,b)不是原始问题。我不会发布有关排序的问题,并且希望得到一个开始于“假设您只想对8种事物进行排序...”的答案。

就是说,如果您知道N实际上限制为8,则这是一种合理的方法。我上面的回答回答了没有此类限制的原始问题。


如果没有额外的内存,则无法一pass而就。如果还有另一个NxN矩阵可以存储结果,则可以一次完成。同样,通过一些旋转和两次通过,可以在没有附加内存的情况下完成。
paxos1977

2
即使您使用临时矩阵,也仍然不能一pass而就,否则我将无法到达这里。您需要通过一遍来推断行/列信息,并需要一遍来设置所有内容。
拉瑟五世卡尔森

通过认识到每行只有一个唯一的非零值,并仅通过引用进行分配,我解决了这个问题。
Daniel Papasian

@ ceretullis,lassevk:我仍然认为不能一pass而就。第二个矩阵上的通过次数必须计数-否则,您可以一次通过复制矩阵,然后按需要使用副本。@Daniel Papasian:您的溶液不能扩展,其中N>在INT /长/不管#bits
Draemon

Draemon,这项技术可以很好地扩展,只是数学而已-您可以构建能够做到这一点的硬件,也可以使用软件技术来处理大于单词大小的数字。两者均未违反问题的限制,恕我直言
Daniel Papasian

10

因此,我的想法是使用最后一行/列中的值作为标志来指示相应列/行中的所有值是否均为1。

使用Zig Zag扫描整个矩阵(最后一行/列除外)。在每个元素上,都将最后一行/列中的值设置为与自己在当前元素中的值的逻辑与。换句话说,如果您命中0,则最后一行/列将设置为0。如果您将其设置为1,则只有在已经为1的情况下,最后一行/列中的值才为1。无论如何,将当前元素设置为0。

完成后,如果相应的列/行填充为1,则最后一行/列应为1。

对最后一行和最后一列进行线性扫描,然后查找1。在最后一行和最后一栏均为1的矩阵主体的相应元素中设置1。

编码将很棘手,以避免出现一一失误的错误等,但是它应该可以一口气工作。


太好了……我在同一行上进行思考,但是错过了使用最后一行/列来存储该信息,因此我被多余的内存卡在了一对Nx1阵列上。
戴夫·谢罗曼

1
对我来说,这似乎像是两次通过-一次是曲折扫描,第二次是“在矩阵主体中相应的元素中设置1,最终行和列均为1”。
亚当·罗森菲尔德

之字形扫描(严格来说,这不是必须的)会遍历所有但最后一行/列。因此,扫描final / row列不会复制先前扫描的元素。因此一通。换句话说,对于N * N矩阵,它是O(N ^ 2)。
Alastair

6

我在这里有一个解决方案,它可以一次运行,并且所有处理都“就地”进行,没有额外的内存(保存来增加堆栈)。

它使用递归来延迟零的写入,这当然会破坏其他行和列的矩阵:

#include <iostream>

/**
* The idea with my algorithm is to delay the writing of zeros
* till all rows and cols can be processed. I do this using
* recursion:
* 1) Enter Recursive Function:
* 2) Check the row and col of this "corner" for zeros and store the results in bools
* 3) Send recursive function to the next corner
* 4) When the recursive function returns, use the data we stored in step 2
*       to zero the the row and col conditionally
*
* The corners I talk about are just how I ensure I hit all the row's a cols,
* I progress through the matrix from (0,0) to (1,1) to (2,2) and on to (n,n).
*
* For simplicities sake, I use ints instead of individual bits. But I never store
* anything but 0 or 1 so it's still fair ;)
*/

// ================================
// Using globals just to keep function
// call syntax as straight forward as possible
int n = 5;
int m[5][5] = {
                { 1, 0, 1, 1, 0 },
                { 0, 1, 1, 1, 0 },
                { 1, 1, 1, 1, 1 },
                { 1, 0, 1, 1, 1 },
                { 1, 1, 1, 1, 1 }
            };
// ================================

// Just declaring the function prototypes
void processMatrix();
void processCorner( int cornerIndex );
bool checkRow( int rowIndex );
bool checkCol( int colIndex );
void zeroRow( int rowIndex );
void zeroCol( int colIndex );
void printMatrix();

// This function primes the pump
void processMatrix() {
    processCorner( 0 );
}

// Step 1) This is the heart of my recursive algorithm
void processCorner( int cornerIndex ) {
    // Step 2) Do the logic processing here and store the results
    bool rowZero = checkRow( cornerIndex );
    bool colZero = checkCol( cornerIndex );

    // Step 3) Now progress through the matrix
    int nextCorner = cornerIndex + 1;
    if( nextCorner < n )
        processCorner( nextCorner );

    // Step 4) Finially apply the changes determined earlier
    if( colZero )
        zeroCol( cornerIndex );
    if( rowZero )
        zeroRow( cornerIndex );
}

// This function returns whether or not the row contains a zero
bool checkRow( int rowIndex ) {
    bool zero = false;
    for( int i=0; i<n && !zero; ++i ) {
        if( m[ rowIndex ][ i ] == 0 )
            zero = true;
    }
    return zero;
}

// This is just a helper function for zeroing a row
void zeroRow( int rowIndex ) {
    for( int i=0; i<n; ++i ) {
        m[ rowIndex ][ i ] = 0;
    }
}

// This function returns whether or not the col contains a zero
bool checkCol( int colIndex ) {
    bool zero = false;
    for( int i=0; i<n && !zero; ++i ) {
        if( m[ i ][ colIndex ] == 0 )
            zero = true;
    }

    return zero;
}

// This is just a helper function for zeroing a col
void zeroCol( int colIndex ) {
    for( int i=0; i<n; ++i ) {
        m[ i ][ colIndex ] = 0;
    }
}

// Just a helper function for printing our matrix to std::out
void printMatrix() {
    std::cout << std::endl;
    for( int y=0; y<n; ++y ) {
        for( int x=0; x<n; ++x ) {
            std::cout << m[y][x] << " ";
        }
        std::cout << std::endl;
    }
    std::cout << std::endl;
}

// Execute!
int main() {
    printMatrix();
    processMatrix();
    printMatrix();
}

2
不错的解决方案,但是从技术上讲,您使用的内存比两个允许的布尔值(尽管在堆栈上)要多。
csl

1
这是> 1次通过。如果在checkRow和checkCol中访问时打印(rowIndex,i)和(i,colIndex),则将看到每个元素被多次访问。
Draemon

Draemon:你是对的,我认为我们需要谜语制作者对“单次通过”的明确定义。如果他确实意味着每个元素只能被访问一次,那么我们需要一个不同的解决方案。
亚当

我想象最初的问题(通过电话游戏传给我们)意味着应该“就地”解决问题,这意味着您没有矩阵的另一个副本。而且,更理想的解决方案甚至不需要像存储这样的临时swap()即可进行处理。
亚当

另外,我有点怀疑约束是否指向生成的机器代码。意思是,我提供的“代码”仅使用2个布尔值。取决于我的编译器所做的优化,可能会内联整个令人毛骨悚然的事情,或者谁知道其他事情。我认为我的解决方案是正确的;)
亚当(

4

我不认为这是可行的。当您在第一个正方形上并且其值为1时,您将无法知道同一行和同一列中其他正方形的值。因此,您必须检查这些值,如果为零,请返回第一个正方形并将其值更改为零。我建议分两步进行操作-第一遍收集有关必须将哪些行和列清零的信息(该信息存储在数组中,因此我们在使用一些额外的内存)。第二遍更改值。我知道这不是您要寻找的解决方案,但我认为这是一个实用的解决方案。您给出的约束使问题无法解决。


我有几乎相同的解决方案(请参见下文),而没有其他数组。它是线性时间(但经过2次)
Piotr Lesnicki

@Piotr:是的,第二遍似乎不可避免。我提出的用于存储行和列信息的数组的引入使该算法更加简单明了,并且由于检查和更改值较少,因此运算速度也更快。在存储和效率之间进行权衡。
博扬

3

我可以使用两个整数变量和两次通过(最多32行和列...)来完成此操作

bool matrix[5][5] = 
{ 
    {1, 0, 1, 1, 0},
    {0, 1, 1, 1, 0},
    {1, 1, 1, 1, 1},
    {1, 0, 1, 1, 1},
    {1, 1, 1, 1, 1}
};

int CompleteRows = ~0;
int CompleteCols = ~0;

// Find the first 0
for (int row = 0; row < 5; ++row)
{
    for (int col = 0; col < 5; ++col)
    {
        CompleteRows &= ~(!matrix[row][col] << row);
        CompleteCols &= ~(!matrix[row][col] << col);
    }
}

for (int row = 0; row < 5; ++row)
    for (int col = 0; col < 5; ++col)
        matrix[row][col] = (CompleteRows & (1 << row)) && (CompleteCols & (1 << col));

这是C#吗?〜是什么意思?
sker

是C ++。 ~反转变量中的所有位。0x00000000变为0x00000000。我基本上从所有开始,并在找到0时清除行/列的位。CompleteCols设置了位2和3,而CompleteRows设置了位2和4(基于0)。
日食

然后,您只需将矩阵中的位设置为与CompleteCols和CompleteRows中的一个相对应。
日食

3

问题可以一口气解决

将矩阵保存在i X j数组中。

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

one each pass save the values of i and j for an element which is 0 in arrays a and b
when first row is scanned a= 1 b = 2,5
when second row is scanned a=1,2 b= 1,2,5
when third row is scanned no change
when fourth row is scanned a= 1,2,4 and b= 1,2,5
when fifth row is scanned no change .

现在将保存在a和b中的i和j的值打印为0,其余所有值为1,即(3,3)(3,4)(5,3)和(5,4)


1

需要两次通过的另一种解决方案是在水平和垂直方向上累加AND:

1 0 1 1 0 | 0
0 1 1 1 0 | 0
1 1 1 1 1 | 1
1 0 1 1 1 | 0
1 1 1 1 1 | 1
----------+
0 0 1 1 0    

我以为我可以使用奇偶校验位汉明码动态编程设计这样的算法,可能使用这两个布尔值作为2位数字,但是我还没有成功。

您能否与您的工程师重新检查问题陈述并让我们知道?如果确实是一个解决方案,我想保持这个问题小打小闹。


1

保留一个变量以跟踪所有与在一起的行。

如果一行为-1(全为1),则使下一行成为对该变量的引用

如果一行中没有任何内容,则为0。您可以一次完成所有操作。伪代码:

foreach (my $row) rows {
     $andproduct = $andproduct & $row;
     if($row != -1) {
        zero out the row
     }  else {
        replace row with a reference to andproduct
     }
}

这样就可以完成一次操作,但是这里有一个假设,即N足够小,CPU可以在单行上执行算术运算,否则您将需要遍历每一行以确定是否全部我相信不是1s。但是考虑到您在询问算法而不是限制我的硬件,我将以“构建支持N位算术的CPU ...”开始回答。

这是一个如何在C中完成此操作的示例。请注意,我认为值和arr一起代表了数组,而p和numproduct是我的迭代器,而AND乘积变量用于实现此问题。(我可以使用指针算术遍历arr来验证我的工作,但是一次就足够了!)

int main() {
    int values[] = { -10, 14, -1, -9, -1 }; /* From the problem spec, converted to decimal for my sanity */
    int *arr[5] = { values, values+1, values+2, values+3, values+4 };
    int **p;
    int numproduct = 127;

    for(p = arr; p < arr+5; ++p) {
        numproduct = numproduct & **p;
        if(**p != -1) {
            **p = 0;
        } else {
            *p = &numproduct;
        }
    }

    /* Print our array, this loop is just for show */
    int i;
    for(i = 0; i < 5; ++i) {
        printf("%x\n",*arr[i]);
    }
    return 0;
}

这将产生0、0、6、0、6,这是给定输入的结果。

或在PHP中,如果人们认为我在C中的堆栈游戏在作弊(我建议你这样做不是,因为我应该能够以任何方式存储矩阵):

<?php

$values = array(-10, 14, -1, -9, -1);
$numproduct = 127;

for($i = 0; $i < 5; ++$i) {
    $numproduct = $numproduct & $values[$i];
    if($values[$i] != -1) {
        $values[$i] = 0;
    } else {
        $values[$i] = &$numproduct;
    }
}

print_r($values);

我想念什么吗?


如果N大于int / long / bit中的位数,则此方法不起作用,因此我认为它不重要。
Draemon

如果0位于数组的底部,也不会捕获任何东西(尝试使用values [] = {-1,-9,-1,14,-10})。

Draemon,我在回答中指出,作为问题的一部分,在没有硬件限制的情况下,您首先要“构建支持N位算术的CPU”。
Daniel Papasian

乔什,我不跟随。使用C或PHP解决方案以及您建议的数组,我得到6 0 6 0 0,我相信这是正确的答案。
Daniel Papasian

@Daniel-不能,因为N不是常数。除了“用1Mbit的单词构建新计算机几乎不是一个合理的算法步骤
。– Draemon

1

不错的挑战。这种解决方案仅使用在堆栈上创建的两个布尔值,但是由于函数是递归的,因此布尔值在堆栈上创建了几次。

typedef unsigned short     WORD;
typedef unsigned char      BOOL;
#define true  1
#define false 0
BYTE buffer[5][5] = {
1, 0, 1, 1, 0,
0, 1, 1, 1, 0,
1, 1, 1, 1, 1,
1, 0, 1, 1, 1,
1, 1, 1, 1, 1
};
int scan_to_end(BOOL *h,BOOL *w,WORD N,WORD pos_N)
{
    WORD i;
    for(i=0;i<N;i++)
    {
        if(!buffer[i][pos_N])
            *h=false;
        if(!buffer[pos_N][i])
            *w=false;
    }
    return 0;
}
int set_line(BOOL h,BOOL w,WORD N,WORD pos_N)
{
    WORD i;
    if(!h)
        for(i=0;i<N;i++)
            buffer[i][pos_N] = false;
    if(!w)
        for(i=0;i<N;i++)
            buffer[pos_N][i] = false;
    return 0;
}
int scan(int N,int pos_N)
{
    BOOL h = true;
    BOOL w = true;
    if(pos_N == N)
        return 0;
    // Do single scan
    scan_to_end(&h,&w,N,pos_N);
    // Scan all recursive before changeing data
    scan(N,pos_N+1);
    // Set the result of the scan
    set_line(h,w,N,pos_N);
    return 0;
}
int main(void)
{
    printf("Old matrix\n");
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[0][0],(WORD)buffer[0][1],(WORD)buffer[0][2],(WORD)buffer[0][3],(WORD)buffer[0][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[1][0],(WORD)buffer[1][1],(WORD)buffer[1][2],(WORD)buffer[1][3],(WORD)buffer[1][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[2][0],(WORD)buffer[2][1],(WORD)buffer[2][2],(WORD)buffer[2][3],(WORD)buffer[2][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[3][0],(WORD)buffer[3][1],(WORD)buffer[3][2],(WORD)buffer[3][3],(WORD)buffer[3][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[4][0],(WORD)buffer[4][1],(WORD)buffer[4][2],(WORD)buffer[4][3],(WORD)buffer[4][4]);
    scan(5,0);
    printf("New matrix\n");
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[0][0],(WORD)buffer[0][1],(WORD)buffer[0][2],(WORD)buffer[0][3],(WORD)buffer[0][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[1][0],(WORD)buffer[1][1],(WORD)buffer[1][2],(WORD)buffer[1][3],(WORD)buffer[1][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[2][0],(WORD)buffer[2][1],(WORD)buffer[2][2],(WORD)buffer[2][3],(WORD)buffer[2][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[3][0],(WORD)buffer[3][1],(WORD)buffer[3][2],(WORD)buffer[3][3],(WORD)buffer[3][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[4][0],(WORD)buffer[4][1],(WORD)buffer[4][2],(WORD)buffer[4][3],(WORD)buffer[4][4]);
    system( "pause" );
    return 0;
}

扫描方式如下:

s,s,s,s,s
s,0,0,0,0
s,0,0,0,0
s,0,0,0,0
s,0,0,0,0


0,s,0,0,0
s,s,s,s,s
0,s,0,0,0
0,s,0,0,0
0,s,0,0,0

等等

然后在每个扫描功能返回时更改此模式中的值。(自下而上):

0,0,0,0,c
0,0,0,0,c
0,0,0,0,c
0,0,0,0,c
c,c,c,c,c


0,0,0,c,0
0,0,0,c,0
0,0,0,c,0
c,c,c,c,c
0,0,0,c,0

等等


我猜这是不正确的,因为您仍然在堆栈中使用两个以上的布尔值。
csl

正如我悲伤的两个布尔值。这是我能想到的最接近他提供的规格。我希望在这里看到一个实际的解决方案。如果可行。
eaanon01

我认为要求不是指增加堆栈。我认为这是一个完全有效的解决方案。
亚当

那也是我的想法。但我无法确定,直到有人发布更好的解决方案。至少我的解决方案是可编译的,任何人都可以验证。:) ...对于实际问题,我没有发现伪代码。Thnx
eaanon01

1

好的,这是一个解决方案

  • 仅使用一个额外的长值作为工作存储。
  • 不使用递归。
  • 只有N的一遍,甚至没有N * N。
  • 将适用于N的其他值,但需要新的#defines。
#include <stdio.h>
#define BIT30 (1<<24)
#define COLMASK 0x108421L
#define ROWMASK 0x1fL
unsigned long long STARTGRID = 
((0x10 | 0x0 | 0x4 | 0x2 | 0x0) << 20) |
((0x00 | 0x8 | 0x4 | 0x2 | 0x0) << 15) |
((0x10 | 0x8 | 0x4 | 0x2 | 0x1) << 10) |
((0x10 | 0x0 | 0x4 | 0x2 | 0x1) << 5) |
((0x10 | 0x8 | 0x4 | 0x2 | 0x1) << 0);


void dumpGrid (char* comment, unsigned long long theGrid) {
    char buffer[1000];
    buffer[0]='\0';
    printf ("\n\n%s\n",comment);
    for (int j=1;j<31; j++) {
        if (j%5!=1)
            printf( "%s%s", ((theGrid & BIT30)==BIT30)? "1" : "0",(((j%5)==0)?"\n" : ",") );    
        theGrid = theGrid << 1;
    }
}

int main (int argc, const char * argv[]) {
    unsigned long long rowgrid = STARTGRID;
    unsigned long long colGrid = rowgrid;

    unsigned long long rowmask = ROWMASK;
    unsigned long long colmask = COLMASK;

    dumpGrid("Initial Grid", rowgrid);
    for (int i=0; i<5; i++) {
        if ((rowgrid & rowmask)== rowmask) rowgrid |= rowmask;
        else rowgrid &= ~rowmask;
        if ((colGrid & colmask) == colmask) colmask |= colmask;
        else colGrid &=  ~colmask;
        rowmask <<= 5;
        colmask <<= 1;
    }
    colGrid &= rowgrid;
    dumpGrid("RESULT Grid", colGrid);
    return 0;
    }

当然,这是一个不错的解决方案。我想这里的每个解决方案都至少忽略了其中一项要求。因此,拥有一个N的最大允许值的解决方案并不是世界上最糟糕的事情,所以在这一点上做得很好:)
Adam

将N限制为8并声称满足一次通过的要求只是愚蠢的。这不是一般的解决方案。问题中没有对N的大小进行限制,因此您只解决了一个子问题。
Draemon

但是所有这些解决方案都以一种或另一种方式限制了N。
AnthonyLambert

说它是N的一遍显然是完全错误的。即使只读取原始矩阵中每个位置的值也为O(N ^ 2),并且最肯定地必须至少读取一次每个位置的值才能计算解。即使您将值长时间存储为单个位,由于存在O(N ^ 2)位,因此访问每个位也将是O(N ^ 2)。
2012年

很明显,RowGrid值存储整个网格是一次传递,如果优化程序有任何好处,则在第一次读取后将成为整个算法的处理器寄存器之一。
安东尼·兰伯特(AnthonyLambert)2012年

1

其实。如果您只想运行算法并打印出结果(即不还原结果),则可以轻松地一遍就完成。麻烦的是,您在运行算法时尝试修改阵列。

这是我的解决方案,它只涉及对Givein(i,j)元素的行/列值进行“与”运算并打印出来。

#include <iostream>
#include "stdlib.h"

void process();

int dim = 5;
bool m[5][5]{{1,0,1,1,1},{0,1,1,0,1},{1,1,1,1,1},{1,1,1,1,1},{0,0,1,1,1}};


int main() {
    process();
    return 0;
}

void process() {
    for(int j = 0; j < dim; j++) {
        for(int i = 0; i < dim; i++) {
            std::cout << (
                          (m[0][j] & m[1][j] & m[2][j] & m[3][j] & m[4][j]) &
                          (m[i][0] & m[i][1] & m[i][2] & m[i][3] & m[i][4])
                          );
        }
        std::cout << std::endl;
    }
}

1

我试图用C#解决此问题。

除了实际矩阵外,我还使用了两个循环变量(i和j),n代表其尺寸

我尝试的逻辑是:

  1. 计算与矩阵的每个同心正方形有关的行和列的AND
  2. 将其存储在其角落单元格中(我已按逆时针顺序存储它们)
  3. 在评估特定正方形时,使用两个布尔变量来保留两个角的值。
  4. 当外部循环(i)在中间时,此过程将结束。
  5. 根据边角单元格评估其他单元格的结果(i的其余部分)。在此过程中跳过角落单元格。
  6. 当我达到n时,除边角单元格外,所有单元格都将具有其结果。
  7. 更新角落单元格。这是对n / 2长度的额外迭代,而不是问题中提到的单遍约束。

码:

void Evaluate(bool [,] matrix, int n)
{
    bool tempvar1, tempvar2;

    for (var i = 0; i < n; i++)
    {
        tempvar1 = matrix[i, i];
        tempvar2 = matrix[n - i - 1, n - i - 1];

        var j = 0;

        for (j = 0; j < n; j++)
        {
            if ((i < n/2) || (((n % 2) == 1) && (i == n/2) && (j <= i)))
            {
                // store the row and col & results in corner cells of concentric squares
                tempvar1 &= matrix[j, i];
                matrix[i, i] &= matrix[i, j];
                tempvar2 &= matrix[n - j - 1, n - i - 1];
                matrix[n - i - 1, n - i - 1] &= matrix[n - i - 1, n - j - 1];
            }
            else
            {
                // skip corner cells of concentric squares
                if ((j == i) || (j == n - i - 1)) continue;

                // calculate the & values for rest of them
                matrix[i, j] = matrix[i, i] & matrix[n - j - 1, j];
                matrix[n - i - 1, j] = matrix[n - i - 1, n - i - 1] & matrix[n - j - 1, j];

                if ((i == n/2) && ((n % 2) == 1))
                {
                    // if n is odd
                    matrix[i, n - j - 1] = matrix[i, i] & matrix[j, n - j - 1];
                }
            }
        }

        if ((i < n/2) || (((n % 2) == 1) && (i <= n/2)))
        {
            // transfer the values from temp variables to appropriate corner cells of its corresponding square
            matrix[n - i - 1, i] = tempvar1;
            matrix[i, n - i - 1] = tempvar2;
        }
        else if (i == n - 1)
        {
            // update the values of corner cells of each concentric square
            for (j = n/2; j < n; j++)
            {
                tempvar1 = matrix[j, j];
                tempvar2 = matrix[n - j - 1, n - j - 1];

                matrix[j, j] &= matrix[n - j - 1, j];
                matrix[n - j - 1, j] &= tempvar2;

                matrix[n - j - 1, n - j - 1] &= matrix[j, n - j - 1];
                matrix[j, n - j - 1] &= tempvar1;
            }
        }
    }
}

1

一遍-我只遍历了一次输入,但是使用了一个新数组和仅两个额外的布尔变量。

public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        sc.nextLine();

        boolean rowDel = false, colDel = false;
        int arr[][] = new int[n][n];
        int res[][] = new int[n][n];
        int i, j;
        for (i = 0; i < n; i++) {

            for (j = 0; j < n; j++) {
                arr[i][j] = sc.nextInt();
                res[i][j] = arr[i][j];  
            }
        }

        for (i = 0; i < n; i++) {

            for (j = 0; j < n; j++) {
                if (arr[i][j] == 0)
                    colDel = rowDel = true; //See if we have to delete the
                                            //current row and column
                if (rowDel == true){
                    res[i] = new int[n];
                    rowDel = false;
                }
                if(colDel == true){
                    for (int k = 0; k < n; k++) {
                        res[k][j] = 0;
                    }
                    colDel = false;
                }

            }

        }

        for (i = 0; i < n; i++) {

            for (j = 0; j < n; j++) {
                System.out.print(res[i][j]);
            }
            System.out.println();
        }
        sc.close();

    }

0

虽然在给定约束的情况下不可能实现,但最节省空间的方式是通过以重叠,交替的行/列方式遍历矩阵,这将形成类似于以锯齿形方式放置砖的模式:

-----
|----
||---
|||--
||||-

使用此方法,您将按照指示进入每个行/列,如果在任何时候遇到0,请设置一个布尔变量,然后重新遍历该行/列,然后将条目设置为0。

这将不需要额外的内存,并且只会使用一个布尔变量。不幸的是,如果将“ far”边设置为全0,那是最坏的情况,并且遍历整个数组两次。


我可能是错的,但是您确定这可行吗?当您处理第三列时,例如,如何知道在处理第一行之前第一行顶部的值是1还是0?
史蒂夫·杰索普

您不知道,但是您也不需要。如果为0,则整个列都必须为0。如果上一行的值为1,则您知道它上面的所有行都是1(并且一直都是)。
戴夫·谢罗曼

0

创建一个结果矩阵并将所有值设置为1。一旦遇到0,就通过数据矩阵,将结果矩阵行列设置为0

在第一遍结束时,结果矩阵将具有正确的答案。

看起来很简单。有没有我想念的把戏?您是否不允许使用结果集?

编辑:

看起来像F#函数,但这有点作弊,因为即使您执行一次遍历,该函数也可以递归进行。

看来,面试官试图弄清楚您是否了解函数式编程。


1
使用结果集将占用额外的内存。
cdeszaq

函数式编程不会修改原始数组。
斯万特

0

好吧,我想出了一个使用4个布尔值和2个循环计数器的单程,就地(非递归)解决方案。我没有设法将其减少到2布尔和2整数,但是如果可能的话,我不会感到惊讶。每个单元大约进行3次读取和3次写入,并且应该为O(N ^ 2)即。数组大小线性。

花了我几个小时来解决这个问题-我不想在面试的压力下提出这个问题!如果我做了个嘘声,我太累了,无法发现它...

嗯...我选择将“单遍”定义为一次扫描矩阵,而不是一次触摸每个值!:-)

#include <stdio.h>
#include <memory.h>

#define SIZE    5

typedef unsigned char u8;

u8 g_Array[ SIZE ][ SIZE ];

void Dump()
{
    for ( int nRow = 0; nRow < SIZE; ++nRow )
    {
        for ( int nColumn = 0; nColumn < SIZE; ++nColumn )
        {
            printf( "%d ", g_Array[ nRow ][ nColumn ] );
        }
        printf( "\n" );
    }
}

void Process()
{
    u8 fCarriedAlpha = true;
    u8 fCarriedBeta = true;
    for ( int nStep = 0; nStep < SIZE; ++nStep )
    {
        u8 fAlpha = (nStep > 0) ? g_Array[ nStep-1 ][ nStep ] : true;
        u8 fBeta = (nStep > 0) ? g_Array[ nStep ][ nStep - 1 ] : true;
        fAlpha &= g_Array[ nStep ][ nStep ];
        fBeta &= g_Array[ nStep ][ nStep ];
        g_Array[ nStep-1 ][ nStep ] = fCarriedBeta;
        g_Array[ nStep ][ nStep-1 ] = fCarriedAlpha;
        for ( int nScan = nStep + 1; nScan < SIZE; ++nScan )
        {
            fBeta &= g_Array[ nStep ][ nScan ];
            if ( nStep > 0 )
            {
                g_Array[ nStep ][ nScan ] &= g_Array[ nStep-1 ][ nScan ];
                g_Array[ nStep-1][ nScan ] = fCarriedBeta;
            }

            fAlpha &= g_Array[ nScan ][ nStep ];
            if ( nStep > 0 )
            {
                g_Array[ nScan ][ nStep ] &= g_Array[ nScan ][ nStep-1 ];
                g_Array[ nScan ][ nStep-1] = fCarriedAlpha;
            }
        }

        g_Array[ nStep ][ nStep ] = fAlpha & fBeta;

        for ( int nScan = nStep - 1; nScan >= 0; --nScan )
        {
            g_Array[ nScan ][ nStep ] &= fAlpha;
            g_Array[ nStep ][ nScan ] &= fBeta;
        }
        fCarriedAlpha = fAlpha;
        fCarriedBeta = fBeta;
    }
}

int main()
{
    memset( g_Array, 1, sizeof(g_Array) );
    g_Array[0][1] = 0;
    g_Array[0][4] = 0;
    g_Array[1][0] = 0;
    g_Array[1][4] = 0;
    g_Array[3][1] = 0;

    printf( "Input:\n" );
    Dump();
    Process();
    printf( "\nOutput:\n" );
    Dump();

    return 0;
}

0

我希望您喜欢我的1-pass C#解决方案

您可以使用O(1)检索元素,只需要矩阵的一行和一列的空间

bool[][] matrix =
{
    new[] { true, false, true, true, false }, // 10110
    new[] { false, true, true, true, false }, // 01110
    new[] { true, true, true, true, true },   // 11111
    new[] { true, false, true, true, true },  // 10111
    new[] { true, true, true, true, true }    // 11111
};

int n = matrix.Length;
bool[] enabledRows = new bool[n];
bool[] enabledColumns = new bool[n];

for (int i = 0; i < n; i++)
{
    enabledRows[i] = true;
    enabledColumns[i] = true;
}

for (int rowIndex = 0; rowIndex < n; rowIndex++)
{
    for (int columnIndex = 0; columnIndex < n; columnIndex++)
    {
        bool element = matrix[rowIndex][columnIndex];
        enabledRows[rowIndex] &= element;
        enabledColumns[columnIndex] &= element;
    }
}

for (int rowIndex = 0; rowIndex < n; rowIndex++)
{
    for (int columnIndex = 0; columnIndex < n; columnIndex++)
    {
        bool element = enabledRows[rowIndex] & enabledColumns[columnIndex];
        Console.Write(Convert.ToInt32(element));
    }
    Console.WriteLine();
}

/*
    00000
    00000
    00110
    00000
    00110
*/

我认为唯一的问题可能是您正在使用两个额外的数据数组来完成这项工作。规定之一是不要使用额外的内存。但是不错!这基本上就是我在回答中所做的:)
肯尼·卡森

0

1个通行证,2个布尔值。我只需要假设迭代中的整数索引不计算在内。

这不是一个完整的解决方案,但我无法通过这一点。

如果我只能确定0是原始0还是转换为0的1,那么我就不必使用-1,这样就可以了。

我的输出是这样的:

-1  0 -1 -1  0
 0 -1 -1 -1  0
-1 -1  1  1 -1
-1  0 -1 -1 -1
-1 -1  1  1 -1

我的方法的独创性是使用检查行或列的前半部分来确定它是否包含0,然后使用后半部分来设置值-这是通过查看x和width-x,然后查看y和height来完成的。 -y在每个迭代中。根据迭代前半部分的结果,如果在行或列中找到0,我将使用迭代的后半部分将1更改为-1。

我只是意识到,只有1个布尔值可以将1留给...?

我正在发布此邮件,希望有人会说:“啊,就这样做吧……”(我花了太多时间在上面不发邮件。)

这是VB中的代码:

Dim D(,) As Integer = {{1, 0, 1, 1, 1}, {0, 1, 1, 0, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {0, 0, 1, 1, 1}}

Dim B1, B2 As Boolean

For y As Integer = 0 To UBound(D)

    B1 = True : B2 = True

    For x As Integer = 0 To UBound(D)

        // Scan row for 0's at x and width - x positions. Halfway through I'll konw if there's a 0 in this row.
        //If a 0 is found set my first boolean to false.
        If x <= (Math.Ceiling((UBound(D) + 1) / 2) - 1) Then
            If D(x, y) = 0 Or D(UBound(D) - x, y) = 0 Then B1 = False
        End If

        //If the boolean is false then a 0 in this row was found. Spend the last half of this loop
        //updating the values. This is where I'm stuck. If I change a 1 to a 0 it will cause the column
        //scan to fail. So for now I change to a -1. If there was a way to change to 0 yet later tell if
        //the value had changed this would work.
        If Not B1 Then
            If x >= (Math.Ceiling((UBound(D) + 1) / 2) - 1) Then
                If D(x, y) = 1 Then D(x, y) = -1
                If D(UBound(D) - x, y) = 1 Then D(UBound(D) - x, y) = -1
            End If
        End If

        //These 2 block do the same as the first 2 blocks but I switch x and y to do the column.
        If x <= (Math.Ceiling((UBound(D) + 1) / 2) - 1) Then
            If D(y, x) = 0 Or D(y, UBound(D) - x) = 0 Then B2 = False
        End If

        If Not B2 Then
            If x >= (Math.Ceiling((UBound(D) + 1) / 2) - 1) Then
                If D(y, x) = 1 Then D(y, x) = -1
                If D(y, UBound(D) - x) = 1 Then D(y, UBound(D) - x) = -1
            End If
        End If

    Next
Next

0

没有人使用二进制形式吗?因为只有1和0。我们可以使用二进制向量。

def set1(M, N):
    '''Set 1/0s on M according to the rules.

    M is a list of N integers. Each integer represents a binary array, e.g.,
    000100'''
    ruler = 2**N-1
    for i,v in enumerate(M):
        ruler = ruler & M[i]
        M[i] = M[i] if M[i]==2**N-1 else 0  # set i-th row to all-0 if not all-1s
    for i,v in enumerate(M):
        if M[i]: M[i] = ruler
    return M

这是测试:

M = [ 0b10110,
      0b01110,
      0b11111,
      0b10111,
      0b11111 ]

print "Before..."
for i in M: print "{:0=5b}".format(i)

M = set1(M, len(M))
print "After..."
for i in M: print "{:0=5b}".format(i)

并输出:

Before...
10110
01110
11111
10111
11111
After...
00000
00000
00110
00000
00110

0

您可以执行以下操作以使用一次通过但使用输入和输出矩阵:

output(x,y) = col(xy) & row(xy) == 2^n

col(xy)包含点的列中的位在哪里xy; row(xy)是包含point的行中的位xyn是矩阵的大小。

然后只需循环输入。可能扩展以提高空间利用率?


0

一矩阵扫描,两个布尔值,无递归。

如何避免第二遍?当零出现在行或列的末尾时,需要第二次通过以清除行或列。

但是这个问题可以解决,因为当我们扫描行#i时,我们已经知道行#i-1的行状态。因此,当我们扫描行#i时,如果需要,我们可以同时清除行#i-1。

相同的解决方案适用于列,但是我们需要同时扫描行和列,而下一次迭代不会更改数据。

需要两个布尔值来存储第一行和第一列的状态,因为它们的值将在算法主要部分执行期间更改。

为了避免添加更多布尔值,我们在矩阵的第一行和第一列中为行和列存储“清除”标志。

public void Run()
{
    const int N = 5;

    int[,] m = new int[N, N] 
                {{ 1, 0, 1, 1, 0 },
                { 1, 1, 1, 1, 0 },
                { 1, 1, 1, 1, 1 },
                { 1, 0, 1, 1, 1 },
                { 1, 1, 1, 1, 1 }};

    bool keepFirstRow = (m[0, 0] == 1);
    bool keepFirstColumn = keepFirstRow;

    for (int i = 1; i < N; i++)
    {
        keepFirstRow = keepFirstRow && (m[0, i] == 1);
        keepFirstColumn = keepFirstColumn && (m[i, 0] == 1);
    }

    Print(m); // show initial setup

    m[0, 0] = 1; // to protect first row from clearing by "second pass"

    // "second pass" is performed over i-1 row/column, 
    // so we use one more index just to complete "second pass" over the 
    // last row/column
    for (int i = 1; i <= N; i++)
    {
        for (int j = 1; j <= N; j++)
        {
            // "first pass" - searcing for zeroes in row/column #i
            // when i = N || j == N it is additional pass for clearing 
            // the previous row/column
            // j >= i because cells with j < i may be already modified 
            // by "second pass" part
            if (i < N && j < N && j >= i) 
            {
                m[i, 0] &= m[i, j];
                m[0, j] &= m[i, j];

                m[0, i] &= m[j, i];
                m[j, 0] &= m[j, i];
            }

            // "second pass" - clearing the row/column scanned 
            // in the previous iteration
            if (m[i - 1, 0] == 0 && j < N)
            {
                m[i - 1, j] = 0;
            }

            if (m[0, i - 1] == 0 && j < N)
            {
                m[j, i - 1] = 0;
            }
        }

        Print(m);
    }

    // Clear first row/column if needed
    if (!keepFirstRow || !keepFirstColumn)
    {
        for (int i = 0; i < N; i++)
        {
            if (!keepFirstRow)
            {
                m[0, i] = 0;
            }
            if (!keepFirstColumn)
            {
                m[i, 0] = 0;
            }
        }
    }

    Print(m);

    Console.ReadLine();
}

private static void Print(int[,] m)
{
    for (int i = 0; i < m.GetLength(0); i++)
    {
        for (int j = 0; j < m.GetLength(1); j++)
        {
            Console.Write(" " + m[i, j]);
        }
        Console.WriteLine();
    }
    Console.WriteLine();
}

0

似乎以下作品不需要额外的空间:

首先请注意,将行的元素乘以元素所在的行的元素会得到所需的值。

为了不使用任何额外的空间(不制作新矩阵并填充它,而是直接对矩阵应用更改),请从矩阵的左上角开始对任何ixi矩阵进行计算(“起始于(0) ,0)),然后再考虑索引> i的任何元素。

希望这行得通(havent testet)


似乎不正确。假设第0行只有1个值。如果您为(0,0)设置的最终值为0,则稍后将完整的行设置为0,这不一定正确。实际上,您需要使用自己的原理为每个单元存储2个值,以实现动态编程风格。
Eyal Schneider

当然,你是对的。除了存储两个值外,我还可以使用第三个可能性,例如-1,它表示“旧”矩阵中为1的单元格,该单元格最终将被0取代。当然,这样,一个必须取绝对乘法后的值。最后,所有-1被0代替。
DFF 2013年

0

这是针对C ++中不同的N进行的测试,并且是:
一个通行证两个布尔值没有递归没有额外的内存为大型N持有
(到目前为止,这里没有一个解决方案可以完成所有这些工作。)

更具体地说,我在逗两个循环计数器是可以的。我有两个const无符号,它们仅存在而不是每次为了可读性而进行计算。外循环的间隔为[0,N],内循环的间隔为[1,n-1]。switch语句在循环中的存在主要是为了非常清楚地表明它实际上只是一次传递。

算法策略:

第一个技巧是让我们从矩阵本身开始一行和一列来累积矩阵的内容,通过将我们从第一行和第一列中真正需要了解的所有信息卸载到两个布尔值中,此内存就变得可用。第二个技巧是通过使用子矩阵及其索引的对称性来一次获得两次通过。

算法简介:

  • 扫描第一行并存储是否都是布尔值,对第一列执行相同操作,将结果存储在第二个布尔值中。
  • 对于不包括第一行和第一列的子矩阵:从左到右,从上到下进行迭代,就像读一段一样。在访问每个元素时,如果反向访问子矩阵,也将访问相应的元素。对于每个访问的元素,其值与行所在的第一行交叉,并且与值与其所在行交叉的第一行。
  • 一旦到达子矩阵的中心,则继续同时访问上述两个元素。但是,现在将被访问元素的值设置为AND,其行与第一列交叉的位置,以及其列与第一行交叉的位置。此后,子矩阵完成。
  • 使用在开始时计算的两个布尔变量将第一行和第一列设置为其正确值。

模板化的C ++实现:

template<unsigned n>
void SidewaysAndRowColumn(int((&m)[n])[n]) {
    bool fcol = m[0][0] ? true : false;
    bool frow = m[0][0] ? true : false;
    for (unsigned d = 0; d <= n; ++d) {
        for (unsigned i = 1; i < n; ++i) {
            switch (d) {
                case 0:
                    frow    = frow && m[d][i];
                    fcol    = fcol && m[i][d];
                    break;
                default:
                {
                    unsigned const rd = n - d;
                    unsigned const ri = n - i;
                    if (d * n + i < rd * n + ri)
                    {
                        m[ d][ 0] &= m[ d][ i];
                        m[ 0][ d] &= m[ i][ d];
                        m[ 0][ i] &= m[ d][ i];
                        m[ i][ 0] &= m[ i][ d];
                        m[rd][ 0] &= m[rd][ri];
                        m[ 0][rd] &= m[ri][rd];
                        m[ 0][ri] &= m[rd][ri];
                        m[ri][ 0] &= m[ri][rd];
                    }
                    else
                    {
                        m[ d][ i] = m[ d][0] & m[0][ i];
                        m[rd][ri] = m[rd][0] & m[0][ri];
                    }
                    break;
                }
                case n:
                    if (!frow)
                        m[0][i] = 0;
                    if (!fcol)
                        m[i][0] = 0;
            };
        }
    }
    m[0][0] = (frow && fcol) ? 1 : 0;
}

0

好的,我意识到这不是完全匹配,但是我使用bool和一个字节而不是两个bool一次通过了它。我也不会保证它的效率,但是这些类型的问题通常需要的不是最佳解决方案。

private static void doIt(byte[,] matrix)
{
    byte zeroCols = 0;
    bool zeroRow = false;

    for (int row = 0; row <= matrix.GetUpperBound(0); row++)
    {
        zeroRow = false;
        for (int col = 0; col <= matrix.GetUpperBound(1); col++)
        {
            if (matrix[row, col] == 0)
            {

                zeroRow = true;
                zeroCols |= (byte)(Math.Pow(2, col));

                // reset this column in previous rows
                for (int innerRow = 0; innerRow < row; innerRow++)
                {
                    matrix[innerRow, col] = 0;
                }

                // reset the previous columns in this row
                for (int innerCol = 0; innerCol < col; innerCol++)
                {
                    matrix[row, innerCol] = 0;
                }
            }
            else if ((int)(zeroCols & ((byte)Math.Pow(2, col))) > 0)
            {
                matrix[row, col] = 0;
            }

            // Force the row to zero
            if (zeroRow) { matrix[row, col] = 0; }
        }
    }
}

0

您可以一次通过排序-如果您不计入以随机访问顺序访问矩阵,那么一开始就消除了一次通过的好处(缓存一致性/内存带宽)。

[编辑:简单但错误的解决方案已删除]

通过分两步进行操作,您应该比任何单遍方法获得更好的性能:一次用于累积行/列信息,另一次用于应用。数组(以行优先顺序)被连贯地访问;对于超过缓存大小(但其行可以容纳在缓存中)的数组,应从内存中读取数据两次,并存储一次:

void fixmatrix2(int M[][], int rows, int cols) {
    bool clearZeroRow= false;
    bool clearZeroCol= false;
    for(int j=0; j < cols; ++j) {
        if( ! M[0][j] ) {
            clearZeroRow= true;
        }
    }
    for(int i=1; i < rows; ++i) { // scan/accumulate pass
        if( ! M[i][0] ) {
            clearZeroCol= true;
        }
        for(int j=1; j < cols; ++j) {
            if( ! M[i][j] ) {
                M[0][j]= 0;
                M[i][0]= 0;
            }
        }
    }
    for(int i=1; i < rows; ++i) { // update pass
        if( M[i][0] ) {
            for(int j=0; j < cols; ++j) {
                if( ! M[j][0] ) {
                    M[i][j]= 0;
                }
            }
        } else {
            for(int j=0; j < cols; ++j) {
                M[i][j]= 0;
            }
        }
        if(clearZeroCol) {
            M[i][0]= 0;
        }
    }
    if(clearZeroRow) {
        for(int j=0; j < cols; ++j) {
            M[0][j]= 0;
        }
    }
}

0

我能想到的最简单的解决方案粘贴在下面。逻辑是记录迭代时将哪一行和哪一列设置为零。

import java.util.HashSet;
import java.util.Set;

public class MatrixExamples {
    public static void zeroOut(int[][] myArray) {
        Set<Integer> rowsToZero = new HashSet<>();
        Set<Integer> columnsToZero = new HashSet<>();

        for (int i = 0; i < myArray.length; i++) { 
            for (int j = 0; j < myArray.length; j++) {
                if (myArray[i][j] == 0) {
                    rowsToZero.add(i);
                    columnsToZero.add(j);
                }
            }
        }

        for (int i : rowsToZero) {
            for (int j = 0; j < myArray.length; j++) {
                myArray[i][j] = 0;
            }
        }

        for (int i : columnsToZero) {
            for (int j = 0; j < myArray.length; j++) {
                myArray[j][i] = 0;
            }
        }

        for (int i = 0; i < myArray.length; i++) { // record which rows and                                             // columns will be zeroed
            for (int j = 0; j < myArray.length; j++) {
                System.out.print(myArray[i][j] + ",");
            if(j == myArray.length-1)
                System.out.println();
            }
        }

    }

    public static void main(String[] args) {
        int[][] a = { { 1, 0, 1, 1, 0 }, { 0, 1, 1, 1, 0 }, { 1, 1, 1, 1, 1 },
                { 1, 0, 1, 1, 1 }, { 1, 1, 1, 1, 1 } };
        zeroOut(a);
    }
}

0

这是我的Ruby实现,其中包含测试,这将占用O(MN)空间。如果我们想要实时更新(例如在找到零时显示结果,而不是等待找到零的第一个循环),我们可以创建另一个类变量,例如@output,每当找到零时就更新@output而不更新@input

require "spec_helper"


class Matrix
    def initialize(input)
        @input  = input
        @zeros  = []
    end

    def solve
        @input.each_with_index do |row, i|          
            row.each_with_index do |element, j|                             
                @zeros << [i,j] if element == 0
            end
        end

        @zeros.each do |x,y|
            set_h_zero(x)
            set_v_zero(y)
        end

        @input
    end


    private 

    def set_h_zero(row)     
        @input[row].map!{0}     
    end

    def set_v_zero(col)
        @input.size.times do |r|
            @input[r][col] = 0
        end
    end
end


describe "Matrix" do
  it "Should set the row and column of Zero to Zeros" do
    input =  [[1, 3, 4, 9, 0], 
              [0, 3, 5, 0, 8], 
              [1, 9, 6, 1, 9], 
              [8, 3, 2, 0, 3]]

    expected = [[0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0],
                [0, 9, 6, 0, 0],
                [0, 0, 0, 0, 0]]

    matrix = Matrix.new(input)

    expect(matrix.solve).to eq(expected)
  end
end

0

下面的代码创建一个大小为m,n的矩阵。首先确定矩阵的尺寸。我想用0..10之间的数字随机填充matrix [m] [n]。然后创建另一个相同尺寸的矩阵,并用-1s(最终矩阵)填充。然后遍历初始矩阵以查看是否会命中0。在命中location(x,y)时,转到最终矩阵,并在行x中填充0s,在列y中填充0s。最后,通读最终矩阵,如果值是-1​​(原始值),则将初始矩阵相同位置的值复制到final。

public static void main(String[] args) {
    int m = 5;
    int n = 4;
    int[][] matrixInitial = initMatrix(m, n); // 5x4 matrix init randomly
    int[][] matrixFinal = matrixNull(matrixInitial, m, n); 
    for (int i = 0; i < m; i++) {
        System.out.println(Arrays.toString(matrixFinal[i]));
    }
}

public static int[][] matrixNull(int[][] matrixInitial, int m, int n) {
    int[][] matrixFinal = initFinal(m, n); // create a matrix with mxn and init it with all -1
    for (int i = 0; i < m; i++) { // iterate in initial matrix
        for (int j = 0; j < n; j++) {
            if(matrixInitial[i][j] == 0){ // if a value is 0 make rows and columns 0
                makeZeroX(matrixFinal, i, j, m, n); 
            }
        }
    }

    for (int i = 0; i < m; i++) { // if value is -1 (original) copy from initial
        for (int j = 0; j < n; j++) {
            if(matrixFinal[i][j] == -1) {
                matrixFinal[i][j] = matrixInitial[i][j];
            }
        }
    }
    return matrixFinal;
}

private static void makeZeroX(int[][] matrixFinal, int x, int y, int m, int n) {
        for (int j = 0; j < n; j++) { // make all row 0
            matrixFinal[x][j] = 0;
        }
        for(int i = 0; i < m; i++) { // make all column 0
            matrixFinal[i][y] = 0; 
        }
}

private static int[][] initMatrix(int m, int n) {

    int[][] matrix = new int[m][n];
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            Random rn = new Random();
            int random = rn.nextInt(10);
            matrix[i][j] = random;
        }
    }

    for (int i = 0; i < m; i++) {
        System.out.println(Arrays.toString(matrix[i]));
    }
    System.out.println("******");
    return matrix;
}

private static int[][] initFinal(int m, int n) {

    int[][] matrix = new int[m][n];
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            matrix[i][j] = -1;
        }
    }
    return matrix;
}

// another approach
/**
 * @param matrixInitial
 * @param m
 * @param n
 * @return
 */
private static int[][] matrixNullNew(int[][] matrixInitial, int m, int n) {
    List<Integer> zeroRowList = new ArrayList<>(); // Store rows with 0
    List<Integer> zeroColumnList = new ArrayList<>(); // Store columns with 0
    for (int i = 0; i < m; i++) { // read through the matrix when you hit 0 add the column to zeroColumnList and add
                                  // the row to zeroRowList
        for (int j = 0; j < n; j++) {
            if (matrixInitial[i][j] == 0) {
                if (!zeroRowList.contains(i)) {
                    zeroRowList.add(i);
                }
                if (!zeroColumnList.contains(j)) {
                    zeroColumnList.add(j);
                }
            }
        }
    }

    for (int a = 0; a < m; a++) {
        if (zeroRowList.contains(a)) { // if the row has 0
            for (int b = 0; b < n; b++) {
                matrixInitial[a][b] = 0; // replace all row with zero
            }
        }
    }

    for (int b = 0; b < n; b++) {
        if (zeroColumnList.contains(b)) { // if the column has 0
            for (int a = 0; a < m; a++) {
                matrixInitial[a][b] = 0; // replace all column with zero
            }
        }
    }
    return matrixInitial;
}

您不对发布的代码提供任何解释或上下文。
亚伦

希望现在更好。谢谢你的提醒。如果您不清楚,我想解释更多。
user3743369

0

这是我的解决方案。从代码中可以看到,给定一个M * N矩阵,一旦检查了该行中的零,它将整个行设置为零。我的解决方案的时间复杂度为O(M * N)。我正在共享整个类,该类具有用于测试的静态填充数组和用于在控制台中查看结果的显示数组方法。

public class EntireRowSetToZero {
    static int arr[][] = new int[3][4];
    static {

    arr[0][0] = 1;
    arr[0][1] = 9;
    arr[0][2] = 2;
    arr[0][3] = 2;

    arr[1][0] = 1;
    arr[1][1] = 5;
    arr[1][2] = 88;
    arr[1][3] = 7;

    arr[2][0] = 0;
    arr[2][1] = 8;
    arr[2][2] = 4;
    arr[2][3] = 4;
}

public static void main(String[] args) {
    displayArr(EntireRowSetToZero.arr, 3, 4);
    setRowToZero(EntireRowSetToZero.arr);
    System.out.println("--------------");
    displayArr(EntireRowSetToZero.arr, 3, 4);


}

static int[][] setRowToZero(int[][] arr) {
    for (int i = 0; i < arr.length; i++) {
        for (int j = 0; j < arr[i].length; j++) {
            if(arr[i][j]==0){
                arr[i]=new int[arr[i].length];
            }
        }

    }
    return arr;
}

static void displayArr(int[][] arr, int n, int k) {

    for (int i = 0; i < n; i++) {

        for (int j = 0; j < k; j++) {
            System.out.print(arr[i][j] + " ");
        }
        System.out.println("");
    }

}

}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.