【中等】542-01 矩阵 01 Matrix

题目

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.

  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.

给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。

两个相邻元素间的距离为 1

  1. 给定矩阵的元素个数不超过 10000。
  2. 给定矩阵中至少有一个元素是 0。
  3. 矩阵中的元素只在四个方向上相邻: 上、下、左、右。

Example1

输入
    0 0 0
    0 1 0
    0 0 0     
输出
    0 0 0
    0 1 0
    0 0 0

Example2

输入
    0 0 0
    0 1 0
    1 1 1
输出
    0 0 0
    0 1 0
    1 2 1

解法

方法一:广度优先搜索

解题思路

对于这样的01矩阵,首先可以判断的是如果矩阵位置上的数值为0,那么距离是0,如果位置上是1,其周围如果有0,那么距离就是1,如果周围没有0,那么就要由其周围的1的最小距离决定了。

可以认为,如果已知0的位置,其周围所有的1的最小距离都是1,这些1周围的1的最小距离都是2,也就是说可以从距离为0开始逐渐递增,根据距离找到所有的点。也就是广度优先搜索的思想。

所以,我们先遍历矩阵找到所有的0,将这些0的位置信息存储到队列中,再对队列中所有的位置点找到距离为1的位置并继续存放在队列中,每一个点查询过后便移出。队列中没有位置点时,说明矩阵的所有位置都查询完,算法也就停止了。

代码

class Solution {
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
        int line = matrix.size(), row = matrix[0].size();
        vector<vector<int>> dist(line, vector<int>(row));
        queue<pair<int, int>> deal;
        for(int i = 0; i < line; ++i){
            for(int j = 0; j < row; ++j){
                dist[i][j] = 0;
                if(matrix[i][j] == 0){
                    deal.push(pair<int,int>(i, j));
                }
            }
        }
        while(!deal.empty()){
            int i = deal.front().first, j = deal.front().second;
            deal.pop();
            // 向上
            if(i>0 && matrix[i-1][j] == 1 && dist[i-1][j] == 0) {
                dist[i-1][j] = dist[i][j]+1;
                deal.push(pair<int, int>(i-1, j));
            }
            // 向下
            if(i+1<line && matrix[i+1][j] == 1 && dist[i+1][j] == 0){
                dist[i+1][j] = dist[i][j]+1;
                deal.push(pair<int, int>(i+1, j));
            }
            // 向左
            if(j>0 && matrix[i][j-1] == 1 && dist[i][j-1] == 0) {
                dist[i][j-1] = dist[i][j]+1;
                deal.push(pair<int, int>(i, j-1));
            }
            // 向右
            if(j+1<row && matrix[i][j+1] == 1 && dist[i][j+1] == 0){
                dist[i][j+1] = dist[i][j]+1;
                deal.push(pair<int, int>(i, j+1));
            }
        }
        return dist;
    }
};

方法二:动态规划

解题思路

与广度优先搜索同样的思路,当位置点为0时,最小距离为0,当位置点为1时,显然其最小距离是由周边的点的距离决定的,符合归纳法的特征,就可以考虑使用动态规划算法。

动态规划是一种决策算法,在矩阵问题中,问题的本质就是从任意一个起点开始,如何行走到达某终点的步数最小,对于一个点1和点0,其路径最短的走法一定是水平向不回头走若干步,竖直方向不回头走若干步,也就是一共有四种走法:先左后上、先右后上、先左后下、先右后下,最大不超过line+row

我们思考这样几个问题:

  1. 一个01矩阵,左上角是0,其他位置不定,可以从任意0为起点出发,向右或向下走到达1,那么对于每一个位置为1的点,最短路径是多少?

  2. 一个01矩阵,右上角是0,其他位置不定,可以从任意0为起点出发,向左或向下走到达1,那么对于每一个位置为1的点,最短路径是多少?

  3. 一个01矩阵,左下角是0,其他位置不定,可以从任意0为起点出发,向右或向上走到达1,那么对于每一个位置为1的点,最短路径是多少?

  4. 一个01矩阵,右下角是0,其他位置不定,可以从任意0为起点出发,向左或向上走到达1,那么对于每一个位置为1的点,最短路径是多少?

可以发现,最终的问题变成,选择哪种走法可以使得距离最小呢?

对于每一种走法,都可以进行一次搜索,每一次搜索都可能会有无法搜索到的部分(白色方格),此时我们视为无法走到,也就是距离超出最远为line+row+1,四个走法得到的距离中的最小值就是最终的结果。

在以从0到1只可以先左后上为例,初始状态为位置为0的点距离为0,决策是选择向左或者向上走,状态转移方程是

f[i][j] = matrix[i][j] * min{f[i+1][j]+1, f[i][j+1]+1}

代码

class Solution {
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
        int line = matrix.size(), row = matrix[0].size();
        vector<vector<int>> dist(line, vector<int>(row,line+row+1));
        for(int i = 0; i < line; ++i){
            for(int j = 0; j < row; ++j){
                if(matrix[i][j] == 0){
                    dist[i][j] = 0;
                }
            }
        }

        for(int i = 0; i < line; ++i){
            for(int j = 0; j < row; ++j){
                if(i>0) dist[i][j] = dist[i][j] < matrix[i][j]*(dist[i-1][j]+1) ? dist[i][j] : matrix[i][j]*(dist[i-1][j]+1);
                if(j>0) dist[i][j] = dist[i][j] < matrix[i][j]*(dist[i][j-1]+1) ? dist[i][j] : matrix[i][j]*(dist[i][j-1]+1);
            }
        }
        for(int i = line-1; i >= 0; --i){
            for(int j = 0; j < row; ++j){
                if(i<line-1) dist[i][j] = dist[i][j] < matrix[i][j]*(dist[i+1][j]+1) ? dist[i][j] : matrix[i][j]*(dist[i+1][j]+1);
                if(j>0) dist[i][j] = dist[i][j] < matrix[i][j]*(dist[i][j-1]+1) ? dist[i][j] : matrix[i][j]*(dist[i][j-1]+1);
            }
        }
        for(int i = 0; i < line; ++i){
            for(int j = row-1; j >= 0; --j){
                if(i>0) dist[i][j] = dist[i][j] < matrix[i][j]*(dist[i-1][j]+1) ? dist[i][j] : matrix[i][j]*(dist[i-1][j]+1);
                if(j<row-1) dist[i][j] = dist[i][j] < matrix[i][j]*(dist[i][j+1]+1) ? dist[i][j] : matrix[i][j]*(dist[i][j+1]+1);
            }
        }
        for(int i = line-1; i >= 0; --i){
            for(int j = row-1; j >= 0; --j){
                if(i<line-1) dist[i][j] = dist[i][j] < matrix[i][j]*(dist[i+1][j]+1) ? dist[i][j] : matrix[i][j]*(dist[i+1][j]+1);
                if(j<row-1) dist[i][j] = dist[i][j] < matrix[i][j]*(dist[i][j+1]+1) ? dist[i][j] : matrix[i][j]*(dist[i][j+1]+1);
            }
        }
        return dist;
    }
};

总结

  1. 在广度优先搜索的写法里面,可以用一个dirs[4][2]={{-1,0},{1,0},{0,-1},{0,1}}来记录上下左右的方向,但是这样写的问题在于每次都要进行一些重复的判断,代码整体运算速度会降低(比如如果是向下移动,只需要判断line是否小于最大行数,而不用判断大于0,但是如果用dirs就要重复判断了)

  2. 对于动态规划算法,可以只搜索左上和右下两种走法也可以得到最终结果,因为对于搜索完左上的矩阵,已经保存了向左和向上的路线信息了,搜索右时可以得到右上信息,搜索下时也可以得到左上信息。

posted @ 2020-04-15 14:28  陌良  阅读(429)  评论(0编辑  收藏  举报