542. 01 Matrix(深搜/广搜/DP的题目)

本文探讨了给定由0和1组成的矩阵中,如何计算每个元素到最近0的最短距离。提供了三种算法解决方案:暴力法、BFS广度优先搜索和动态规划,并分析了各自的复杂度。

Given a matrix consists of 0 and 1, find the distance of the nearest 0 for each cell.

The distance between two adjacent cells is 1.

Example 1: 
Input:

0 0 0
0 1 0
0 0 0
Output:
0 0 0
0 1 0
0 0 0

Example 2: 
Input:

0 0 0
0 1 0
1 1 1
Output:
0 0 0
0 1 0
1 2 1

Note:

  1. The number of elements of the given matrix will not exceed 10,000.
  2. There are at least one 0 in the given matrix.
  3. The cells are adjacent in only four directions: up, down, left and right.
解答(摘自Solution):
Approach #1 Brute force [Time Limit Exceeded]

Intuition

Do what the question says.

Algorithm

  • Initialize dist[i][j]=INT_MAX for all {i,j} cells.
  • Iterate over the matrix.
  • If cell is 0dist[i][j]=0,
  • Else, for each 1 cell,
    • Iterate over the entire matrix
    • If the cell is 0, calculate its distance from current cell as abs(k-i)+abs(l-j).
    • If the distance is smaller than the current distance, update it.

C++

vector<vector<int> > updateMatrix(vector<vector<int> >& matrix)
{
    int rows = matrix.size();
    if (rows == 0)
        return matrix;
    int cols = matrix[0].size();
    vector<vector<int> > dist(rows, vector<int>(cols, INT_MAX));
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (matrix[i][j] == 0)
                dist[i][j] = 0;
            else {
                for (int k = 0; k < rows; k++)
                    for (int l = 0; l < cols; l++)
                        if (matrix[k][l] == 0) {
                            int dist_01 = abs(k - i) + abs(l - j);
                            dist[i][j] = min(dist[i][j], abs(k - i) + abs(l - j));
                        }
            }
        }
    }
    return dist;
}

Complexity Analysis

  • Time complexity: O((r \cdot c)^2)O((rc)2). Iterating over the entire matrix for each 1 in the matrix.

  • Space complexity: O(r \cdot c)O(rc). No extra space required than the vector<vector<int> > dist


Approach #2 Using BFS [Accepted]

Intuition

A better brute force: Looking over the entire matrix appears wasteful and hence, we can use Breadth First Search(BFS) to limit the search to the nearest 0found for each 1. As soon as a 0 appears during the BFS, we know that the 0 is nearest, and hence, we move to the next 1.

Think again: But, in this approach, we will only be able to update the distance of one 1 using one BFS, which could in fact, result in slightly higher complexity than the Approach #1 brute force. But hey,this could be optimised if we start the BFS from 0s and thereby, updating the distances of all the 1s in the path.

Algorithm

  • For our BFS routine, we keep a queue, q to maintain the queue of cells to be examined next.
  • We start by adding all the cells with 0s to q.
  • Intially, distance for each 0 cell is 0 and distance for each 1 is INT_MAX, which is updated during the BFS.
  • Pop the cell from queue, and examine its neighbours. If the new calculated distance for neighbour {i,j} is smaller, we add {i,j} to q and update dist[i][j].

C++

vector<vector<int> > updateMatrix(vector<vector<int> >& matrix)
{
    int rows = matrix.size();
    if (rows == 0)
        return matrix;
    int cols = matrix[0].size();
    vector<vector<int> > dist(rows, vector<int>(cols, INT_MAX));
    queue<pair<int, int> > q;
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            if (matrix[i][j] == 0) {
                dist[i][j] = 0;
                q.push({ i, j }); //Put all 0s in the queue.
            }

    int dir[4][2] = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
    while (!q.empty()) {
        pair<int, int> curr = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int new_r = curr.first + dir[i][0], new_c = curr.second + dir[i][1];
            if (new_r >= 0 && new_c >= 0 && new_r < rows && new_c < cols) {
                if (dist[new_r][new_c] > dist[curr.first][curr.second] + 1) {
                    dist[new_r][new_c] = dist[curr.first][curr.second] + 1;
                    q.push({ new_r, new_c });
                }
            }
        }
    }
    return dist;
}

Complexity analysis

  • Time complexity: O(r \cdot c)O(rc).
  • Since, the new cells are added to the queue only if their current distance is greater than the calculated distance, cells are not likely to be added multiple times.

  • Space complexity: O(r \cdot c)O(rc). Additional O(r \cdot c)O(rc) for queue than in Approach #1


Approach #3 DP Approach [Accepted]

Intuition

The distance of a cell from 0 can be calculated if we know the nearest distance for all the neighbours, in which case the distance is minimum distance of any neightbour + 1. And, instantly, the word come to mind DP!!
For each 1, the minimum path to 0 can be in any direction. So, we need to check all the 4 direction. In one iteration from top to bottom, we can check left and top directions, and we need another iteration from bottom to top to check for right and bottom direction.

Algorithm

  • Iterate the matrix from top to bottom-left to right:
  • Update \text{dist}[i][j]=\min(\text{dist}[i][j],\min(\text{dist}[i][j-1],\text{dist}[i-1][j])+1)dist[i][j]=min(dist[i][j],min(dist[i][j1],dist[i1][j])+1) i.e., minimum of the current dist and distance from top or left neighbour +1, that would have been already calculated previously in the current iteration.
  • Now, we need to do the back iteration in the similar manner: from bottom to top-right to left:
  • Update \text{dist}[i][j]=\min(\text{dist}[i][j],\min(\text{dist}[i][j+1],\text{dist}[i+1][j])+1)dist[i][j]=min(dist[i][j],min(dist[i][j+1],dist[i+1][j])+1) i.e. minimum of current dist and distances calculated from bottom and right neighbours, that would be already available in current iteration.

C++

vector<vector<int> > updateMatrix(vector<vector<int> >& matrix)
{
    int rows = matrix.size();
    if (rows == 0)
        return matrix;
    int cols = matrix[0].size();
    vector<vector<int> > dist(rows, vector<int>(cols, INT_MAX - 100000));

    //First pass: check for left and top
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (matrix[i][j] == 0)
                dist[i][j] = 0;
            else {
                if (i > 0)
                    dist[i][j] = min(dist[i][j], dist[i - 1][j] + 1);
                if (j > 0)
                    dist[i][j] = min(dist[i][j], dist[i][j - 1] + 1);
            }
        }
    }

    //Second pass: check for bottom and right
    for (int i = rows - 1; i >= 0; i--) {
        for (int j = cols - 1; j >= 0; j--) {
            if (i < rows - 1)
                dist[i][j] = min(dist[i][j], dist[i + 1][j] + 1);
            if (j < cols - 1)
                dist[i][j] = min(dist[i][j], dist[i][j + 1] + 1);
        }
    }

    return dist;
}

Complexity analysis

  • Time complexity: O(r \cdot c)O(rc). 2 passes of r \cdot crc each
  • Space complexity: O(r \cdot c)O(rc). No additional space required than dist vector<vector<int> >
注:
暴力法很好懂,DFS个人不感兴趣。。。DP的想法是:对于某一个位置,它到最近的0的距离=它的邻居中离这个0最近的那个+1(如果邻居有0那么直接赋0就好了)。注意第一趟要检查的是往上和往左,第二趟是下、右,不能混起来。。
其实我比较关心的是DFS怎么做(来自Discuss,Java版):
public class Solution {
    public int[][] updateMatrix(int[][] matrix) {
        if(matrix.length==0) return matrix;
        
        for(int i = 0; i<matrix.length; i++)
            for(int j = 0; j<matrix[0].length; j++)
                if(matrix[i][j]==1&&!hasNeiberZero(i, j,matrix)) 
                    matrix[i][j] = matrix.length+matrix[0].length+1;
        
        for(int i = 0; i<matrix.length; i++)
            for(int j = 0; j<matrix[0].length; j++)
                if(matrix[i][j]==1)
                    dfs(matrix, i, j, -1);
        
        return matrix;
    }
    private void dfs(int[][] matrix, int x, int y, int val){
        if(x<0||y<0||y>=matrix[0].length||x>=matrix.length||matrix[x][y]<=val)
            return;
        
        if(val>0) matrix[x][y] = val;
        
        dfs(matrix, x+1, y, matrix[x][y]+1);
        dfs(matrix, x-1, y, matrix[x][y]+1);
        dfs(matrix, x, y+1, matrix[x][y]+1);
        dfs(matrix, x, y-1, matrix[x][y]+1);
        
    }
    private boolean hasNeiberZero(int x, int y, int[][] matrix){
        if(x>0&&matrix[x-1][y]==0) return true;
        if(x<matrix.length-1&&matrix[x+1][y]==0) return true;
        if(y>0&&matrix[x][y-1]==0) return true;
        if(y<matrix[0].length-1&&matrix[x][y+1]==0) return true;
        
        return false;
    }
}
这个版本要清晰一点:
public class Solution {
    
    int currMin = 12000;    // The number of elements of the given matrix will not exceed 10,000, so we can use 12,000 (or anything >10,000) throughout the program as MAX value
    
    public int[][] updateMatrix(int[][] matrix) {
        if (matrix.length == 0 || matrix[0].length == 0) {
            return matrix;
        }
        
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                if (matrix[i][j] == 1) {
                    currMin = 12000;
                    int minDist = dfs(matrix, i, j, 0);
                    if (minDist < 12000)
                        matrix[i][j] = minDist;
                }
            }
        }
        
        return matrix;
    }
    
    
    private int dfs(int[][] matrix, int i, int j, int steps) {
        if (i < 0 || i >= matrix.length || j < 0 || j >= matrix[0].length ) {
            return 12000;
        }
        if (matrix[i][j] != 1 || steps > currMin) {
            return matrix[i][j];
        }
        matrix[i][j] ^= 12000;
        int down =    dfs(matrix, i+1, j, steps + 1);
        int up =  dfs(matrix, i-1, j, steps + 1);
        int left =  dfs(matrix, i, j-1, steps + 1);
        int right = dfs(matrix, i, j+1, steps + 1);
        matrix[i][j] ^= 12000;
        
        int minVal = Math.min(left, Math.min(right, Math.min(up, down))) + 1;
        currMin = minVal;
        
        return minVal;
    }
}
15 days ago reply quote 


另一个版本的DFS:
class Solution {
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
        for(int row=0;row<matrix.size();row++){
            for(int col=0;col<matrix[0].size();col++){
                if(matrix[row][col]==1&&no_adjacent_zero(matrix,row,col))
                    matrix[row][col]=10000;
            }
        }
        for(int row=0;row<matrix.size();row++){
            for(int col=0;col<matrix[0].size();col++){
                dfs(matrix,row,col);
            }
        }
        return matrix;
    }
    bool no_adjacent_zero(vector<vector<int>>& matrix,int row,int col){
        int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
        for(int k=0;k<4;k++){
            int new_row=row+next[k][0],new_col=col+next[k][1];
            if(new_row<0||new_row>=matrix.size()||new_col<0||new_col>=matrix[0].size())
                continue;
            if(matrix[new_row][new_col]==0)
                return false;
        }
        return true;
    }
    void dfs(vector<vector<int>>& matrix,int row,int col){
        int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
        for(int k=0;k<4;k++){
            int new_row=row+next[k][0],new_col=col+next[k][1];
            if(new_row<0||new_row>=matrix.size()||new_col<0||new_col>=matrix[0].size())
                continue;
            if(matrix[new_row][new_col]>matrix[row][col]+1){
                matrix[new_row][new_col]=matrix[row][col]+1;
                dfs(matrix,new_row,new_col);
            }
        }
    }
};
所以这里有一个重点就是周围没有0的点要用一个比较大的值覆盖。上一个DFS的解法是只要有1就赋一个大的值。。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值