Microsoft Interview: transforming a matrix

天涯浪子 提交于 2019-12-02 15:17:50

Here is a solution in python pseudocode that uses 2 extra bools of storage. I think it is more clear than I could do in English.

def scanRow(i):
    return 0 if row i is all zeroes, else 1

def scanColumn(j):
    return 0 if col j is all zeroes, else 1

# we're going to use the first row and column
# of the matrix to store row and column scan values,
# but we need aux storage to deal with the overlap
firstRow = scanRow(0)
firstCol = scanCol(0)

# scan each column and store result in 1st row - O(mn) work
for col in range(1, n):
    matrix[0, col] = scanColumn(col)

# now row 0 tells us whether each column is all zeroes or not
# it's also the correct output unless row 0 contained a 1 originally

# do the same for rows into column 0 - O(mn) work
for row in range(1, m):
    matrix[row, 0] = scanRow(row)

matrix[0,0] = firstRow or firstCol

# now deal with the rest of the values - O(mn) work
for row in range(1, m):
    for col in range(1, n):
        matrix[row, col] = matrix[0, col] or matrix[row, 0]


# 3 O(mn) passes!

# go back and fix row 0 and column 0
if firstRow:
    # set row 0 to all ones

if firstCol:
    # set col 0 to all ones

Here's another intuition that gives a clean and simple algorithm for solving the problem.

An initial algorithm using O(n) space.

For now, let's ignore the O(1) memory constraint. Suppose that you can use O(n) memory (if the matrix is m × n). That would make this problem a lot easier and we could use the following strategy:

  • Create an boolean array with one entry per column.
  • For each column, determine whether there are any 1's in the column and store that information in the appropriate array entry.
  • For each row, set that row to be all 1's if there are any 1's in the row.
  • For each column, set that column to be all 1's if the corresponding array entry is set.

As an example, consider this array:

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

We'd start off by creating and populating the auxiliary array, which can be done in time O(mn) by visiting each column one at a time. This is shown here:

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

1 1 1 1 0  <--- aux array

Next, we iterate across the rows and fill each one in if it contains any 1's. This gives this result:

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

1 1 1 1 0  <--- aux array

Finally, we fill in each column with 1's if the auxiliary array has a 1 in that position. This is shown here:

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

1 1 1 1 0  <--- aux array

So there's one problem: this uses O(n) space, which we don't have! So why even go down this route?

A revised algorithm using O(1) space.

It turns out that we can use a very cute trick to run this algorithm using O(1) space. We need a key observation: if every row contains at least one 1, then the entire matrix becomes 1's. We therefore start off by seeing if this is the case. If it is, great! We're done.

Otherwise, there must be some row in the matrix that is all 0's. Since this row is all 0's, we know that in the "fill each row containing a 1 with 1's" step, the row won't be filled in. Therefore, we can use that row as our auxiliary array!

Let's see this in action. Start off with this:

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

Now, we can find a row with all 0's in it and use it as our auxiliary array:

1 1 0 1 0
0 0 0 0 0  <-- Aux array
0 1 0 0 0
1 0 1 1 0

We now fill in the auxiliary array by looking at each column and marking which ones contain at least one 1:

1 1 0 1 0
1 1 1 1 0  <-- Aux array
0 1 0 0 0
1 0 1 1 0

It's perfectly safe to fill in the 1's here because we know that they're going to get filled in anyway. Now, for each row that contains a 1, except for the auxiliary array row, we fill in those rows with 1's:

1 1 1 1 1
1 1 1 1 0  <-- Aux array
1 1 1 1 1
1 1 1 1 1

We skip the auxiliary array because initially it was all 0's, so it wouldn't normally be filled. Finally, we fill in each column with a 1 in the auxiliary array with 1's, giving this final result:

1 1 1 1 1
1 1 1 1 0  <-- Aux array
1 1 1 1 1 
1 1 1 1 1

Let's do another example. Consider this setup:

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

We begin by finding a row that's all zeros, as shown here:

1 0 0 0
0 0 1 0
0 0 0 0 <-- Aux array
0 0 1 0

Next, let's populate that row by marking columns containing a 1:

1 0 0 0
0 0 1 0
1 0 1 0 <-- Aux array
0 0 1 0

Now, fill in all rows containing a 1:

1 1 1 1
1 1 1 1
1 0 1 0 <-- Aux array
1 1 1 1

Next, fill in all columns containing a 1 in the aux array with 1's. This is already done here, and we have our result!

As another example, consider this array:

1 0 0
0 0 1
0 1 0

Every row here contains at least one 1, so we just fill the matrix with ones and are done.

Finally, let's try this example:

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

We have lots of choices for aux arrays, so let's pick the first row:

0 0 0 0 0 <-- aux array
0 0 0 0 0
0 1 0 0 0
0 0 0 0 0
0 0 0 1 0

Now, we fill in the aux array:

0 1 0 1 0 <-- aux array
0 0 0 0 0 
0 1 0 0 0
0 0 0 0 0
0 0 0 1 0

Now, we fill in the rows:

0 1 0 1 0 <-- aux array
0 0 0 0 0 
1 1 1 1 1
0 0 0 0 0
1 1 1 1 1

Now, we fill in the columns based on the aux array:

0 1 0 1 0 <-- aux array
0 1 0 1 0 
1 1 1 1 1
0 1 0 1 0
1 1 1 1 1

And we're done! The whole thing runs in O(mn) time because we

  • Do O(mn) work to find the aux array, and possibly O(mn) work immediately if one doesn't exist.
  • Do O(mn) work to fill in the aux array.
  • Do O(mn) work to fill in rows containing 1s.
  • Do O(mn) work to fill in columns containing 1s.

Plus, it only uses O(1) space, since we just need to store the index of the aux array and enough variables to do loops over the matrix.

EDIT: I have a Java implementation of this algorithm with comments describing it in detail available on my personal site. Enjoy!

Hope this helps!

Assuming matrix is 0-based, i.e. the first element is at mat[0][0]

  1. Use the first row and first column as table headers to contain column and row info respectively. 1.1 Note the element at mat[0][0]. If it is 1, it will require special handling at the end (described later)

  2. Now, start scanning the inner matrix from index[1][1] up to the last element 2.1 If the element at[row][col] == 1 then update the table header data as follows Row: mat[row][0] = 1; Column: mat[0][col] = 1;

At this point we have the complete info on which column and row should be set to 1

  1. Again start scanning the inner matrix starting from mat[1][1] and set each element to 1 if either the current row or column contains 1 in the table header: if ( (mat[row][0] == 1) || (mat[0][col] == 1) ) then set mat[row][col] to 1.

At this point we have processed all the cells in the inner matrix and we are yet to process the table header itself

  1. Process the table header If the matt[0][0] == 1 then set all the elements in the first column and first row to 1
  2. Done

Time complexity O(2*((n-1)(m-1)+(n+m-1)), i.e. O(2*n*m - (n+m) + 1), i.e. O(2*n*m) Space O(1)

See my implementation at http://codepad.org/fycIyflw

Another solution would be to scan the matrix as usual, and at the first 1 you split the matrix in 4 quadrants. You then set the line and the column to 1's, and recursively process each quadrant. Just make sure to set the whole columns and rows, even though you are scanning only a quadrant.

public void setOnes(int [][] matrix){

    boolean [] row = new boolean [matrix.length]
    boolean [] col = new boolean [matrix[0].length]
    for (int i=0;i<matrix.length;i++){
        for(int j=0;j<matrix[0].length;j++){
            if (matrix[i][j] == 1){
                row[i] = true
                col[j] = true
            }
        }
    }

    for (int i=0;i<matrix.length;i++){
        for(int j=0;j<matrix[0].length;j++){
            if (row[i] || col[j]){
                matrix[i][j] = 1;
            }
        }
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!