LeetCode——542. 01 矩阵

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

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

示例 1:
输入:

0 0 0
0 1 0
0 0 0

输出:

0 0 0
0 1 0
0 0 0

示例 2:
输入:

0 0 0
0 1 0
1 1 1

输出:

0 0 0
0 1 0
1 2 1

注意:

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

我们可以首先遍历一次矩阵,将值为0的点都存入queue,将值为1的点改为INT_MAX。然后开始BFS遍历,从queue中取出一个数字,遍历其周围四个点,如果越界或者周围点的值小于等于当前值加1,则直接跳过。因为周围点的距离更小的话,就没有更新的必要,否则将周围点的值更新为当前值加1,然后把周围点的坐标加入queue,参见代码如下:

解法一:

class Solution {
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        vector<vector<int>> dirs{{0,-1},{-1,0},{0,1},{1,0}};
        queue<pair<int, int>> q;
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (matrix[i][j] == 0) q.push({i, j});
                else matrix[i][j] = INT_MAX;
            }
        }
        while (!q.empty()) {
            auto t = q.front(); q.pop();
            for (auto dir : dirs) {
                int x = t.first + dir[0], y = t.second + dir[1];
                if (x < 0 || x >= m || y < 0 || y >= n || matrix[x][y] <= matrix[t.first][t.second] + 1) continue;
                matrix[x][y] = matrix[t.first][t.second] + 1;
                q.push({x, y});
            }
        }
        return matrix;
    }
};

下面这种解法是参考的qswawrq大神的帖子,他想出了一种二次扫描的解法,从而不用使用BFS了。这种解法也相当的巧妙,我们首先建立一个和matrix大小相等的矩阵res,初始化为很大的值,这里我们用INT_MAX-1,为甚么要减1呢,后面再说。然后我们遍历matrix矩阵,当遇到为0的位置,我们将结果res矩阵的对应位置也设为0,这make sense吧,就不多说了。然后就是这个解法的精髓了,如果不是0的地方,我们在第一次扫描的时候,比较其左边和上边的位置,取其中较小的值,再加上1,来更新结果res中的对应位置。这里就明白了为啥我们要初始化为INT_MAX-1了吧,因为这里要加1,如果初始化为INT_MAX就会整型溢出,不过放心,由于是取较小值,res[i][j]永远不会取到INT_MAX,所以不会有再加1溢出的风险。第一次遍历我们比较了左和上的方向,那么我们第二次遍历就要比较右和下的方向,注意两种情况下我们不需要比较,一种是当值为0时,还有一种是当值为1时,这两种情况下值都不可能再变小了,所以没有更新的必要,参见代码如下:

解法二:

class Solution {
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        vector<vector<int>> res(m, vector<int>(n, INT_MAX - 1));
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (matrix[i][j] == 0) res[i][j] = 0;
                else {
                    if (i > 0) res[i][j] = min(res[i][j], res[i - 1][j] + 1);
                    if (j > 0) res[i][j] = min(res[i][j], res[i][j - 1] + 1);
                }
            }
        }
        for (int i = m - 1; i >= 0; --i) {
            for (int j = n - 1; j >= 0; --j) {
                if (res[i][j] != 0 && res[i][j] != 1) {
                    if (i < m - 1) res[i][j] = min(res[i][j], res[i + 1][j] + 1);
                    if (j < n - 1) res[i][j] = min(res[i][j], res[i][j + 1] + 1);
                }
            }
        }
        return res;
    }
};

方法 1:暴力【超时】

想法

按照题目描述暴力枚举

算法

初始化 dist [i] [j]=INT_MAX 对所有的 {i,j} 。
搜索整个矩阵。
如果方格是 0,dist [i] [j]=0,
否则,对于每个为 1 的方格,
搜索整个矩阵
如果某个为 0 的节点,计算到当前方格的距离 abs(k-i)+abs(l-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));
    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;
}

复杂度分析

复杂度分析

时间复杂度:O((r?c)^2),对于每个为 1 的节点都需要遍历一遍整个矩阵。
空间复杂度:O(r?c),除去 vector<vector > dist 外不需要更多的额外空间开销。

方法 2:广度优先搜索

想法

一种更好的暴力:

搜寻整个矩阵太过于浪费,因此我们使用广度优先搜索去限制搜索空间。只要 0 出现在广度优先搜索时,我们就知道这个 0 是最近的。因此,只需要考虑下一个 1。

再次考虑:

但在这种方法中,我们只能够更新一个 1 的距离,这可能在某种程度上,更大程度提高方法 1 的复杂度。但是,如果我们从 0 开始广度优先搜索并且更新路径上所有的 1,这会优化我们的算法。

算法

对于广度优先搜索,保存一个队列 q 维护下一个需要检查的点。

我们将所有的 0 放入 q 中。

初始地,对于每个为 0 的节点距离都是 1,对于所有的 1 都是 INT_MAX,会根据广搜的结果更新。

弹出队列中的元素,检查它的邻居。如果对于邻居 {i,j} 新计算的距离更小,将 {i,j} 加入队列 q 中同时更新距离 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;
}

复杂度分析

时间复杂度:O(r?c),因为每个节点只会加入队列一次,不会多次加入。
空间复杂度:O(r?c),比方法 1 需要多一个队列开销。

方法 3:动态规划

想法

对于一个节点来说,它到 0 的距离可以通过邻居的最近距离计算,在这种情况下最近距离是邻居的距离 + 1。因此,这就让我们想到了动态规划!

对于每个 1,到 0 的最短路径可能从任意方向。所以我们需要检查所有 4 个方向。在从上到下的迭代中,我们需要检查左边和上方的最短路径;我们还需要另一个从下往上的循环,检查右边和下方的方向。

算法

。 从上至下、从左至右迭代整个矩阵:

? 。 更新

? 。dist [i] [j] = min(dist [i] [j], min(dist [i] [j-1], dist [i-1] [j])+1)

最近距离考虑上方邻居和左侧邻居距离 + 1,这在前面的迭代中已经计算完成。

。从下到上、从右至左迭代整个矩阵:

? 。更新

? 。dist [i] [j]=min(dist [i] [j],min(dist [i] [j+1],dist[i+1] [j])+1)

最近距离考虑下方邻居和右侧邻居距离 + 1,这在前面的迭代中已经计算完成。

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;
}

复杂度分析

时间复杂度:O(r?c),两边扫描各为 cr?c。
空间复杂度:O(r?c),除了 dist vector<vector< int > > 无需额外空间。

每个非0点到0的距离,跟它上下左右到0的距离有关,所以第一次遍历先将左上两个位置的非0点最小者加1更改。

这样就是从左上往右下看,非0点到0的最短距离,但是这样还不知道从右下到左上看的最短距离。

所以再从右下到左上遍历,注意这次更新除了要取右下两个点的最短距离之外,还要跟当前位置的点做一次最小值比较,因为右下可能距离更远。

class Solution:
    def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]:
        for i in range(len(matrix)):
            for j in range(len(matrix[0])):
                l,t= 10001,10001
                if matrix[i][j] != 0:
                    if i > 0:
                        t = matrix[i - 1][j]

                    if j > 0:
                        l = matrix[i][j - 1]

                    matrix[i][j] = min(l,t) + 1

        for i in range(len(matrix) - 1, -1 ,-1):
            for j in range(len(matrix[0]) - 1, -1, -1):
                r,b = 10001,10001
                if matrix[i][j] != 0:
                    if i < len(matrix) - 1:
                        b = matrix[i + 1][j]

                    if j < len(matrix[0]) - 1:
                        r = matrix[i][j + 1]

                    matrix[i][j] = min(matrix[i][j], min(r,b) + 1)
        return matrix

原文地址:https://www.cnblogs.com/wwj99/p/12275886.html

时间: 2024-11-03 21:21:06

LeetCode——542. 01 矩阵的相关文章

Leetcode 542.01矩阵

01矩阵 给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离. 两个相邻元素间的距离为 1 . 示例 1: 输入: 0 0 0 0 1 0 0 0 0 输出: 0 0 0 0 1 0 0 0 0 示例 2: 输入: 0 0 0 0 1 0 1 1 1 输出: 0 0 0 0 1 0 1 2 1 注意: 给定矩阵的元素个数不超过 10000. 给定矩阵中至少有一个元素是 0. 矩阵中的元素只在四个方向上相邻: 上.下.左.右. 思路 先把所有0入队,把1置为MAX_VALUE,然

[leetcode] 542. 01 Matrix (Medium)

给予一个矩阵,矩阵有1有0,计算每一个1到0需要走几步,只能走上下左右. 解法一: 利用dp,从左上角遍历一遍,再从右下角遍历一遍,dp存储当前位置到0的最短距离. 十分粗心的搞错了col和row,改了半天---- Runtime: 132 ms, faster than 98.88% of C++ online submissions for 01 Matrix. class Solution { public: vector<vector<int>> updateMatrix(

LeetCode 542. 01 Matrix

输入:只包含0,1的矩阵 输出:元素1到达最近0的距离 算法思想:广度优先搜索. 元素为0为可达区域,元素为1为不可达区域,我们的目标是为了从可达区域不断地扩展至不可达区域,在扩展的过程中,也就计算出了这些不可达区域到达最近可达区域的距离. 每个可达元素都记录了到当前位置的距离,因此在后续的遍历中,如果是经由当前节点到达的下一节点,这个距离会被累加. 1 #include <iostream> 2 #include <vector> 3 #include <queue>

542. 01 矩阵

题目描述:给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离.两个相邻元素间的距离为 1 . 示例 1:输入: 0 0 00 1 00 0 0输出: 0 0 00 1 00 0 0示例 2:输入: 0 0 00 1 01 1 1输出: 0 0 00 1 01 2 1 思路: 题目给出了多个1,要找出每个1到0的最近曼哈顿距离.由于1到0的距离和0到1的距离一样的,所以其实我们可以换个思维:找出每个0到1的距离.因此,题目可以抽象成:多个起始点的BFS class Solutio

Leetcode 542:01 矩阵 01

Leetcode 542:01 矩阵 01 Matrix### 题目: 给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离. 两个相邻元素间的距离为 1 . 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: 输入: 0 0 0 0 1 0 0 0 0 输出

ACDream 1213 Matrix Multiplication (01矩阵处理)

Matrix Multiplication Time Limit: 2000/1000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others) Submit Statistic Next Problem Problem Description Let us consider undirected graph G = {V; E} which has N vertices and M edges. Incidence matrix of

HNU 10111 0-1矩阵

http://acm.hnu.cn/online/?action=problem&type=show&id=10111 题意:中文 题解:在龙哥的帮助下正了二分图匹配的三观--以前的理解繁琐,或者有点儿错吧--二分图匹配从左往右匹配,找增广路.顶点数和match()不需要那么麻烦. 1 // 2 // main.cpp 3 // POJ 3041 4 // 5 // Created by zhang on 14-4-16. 6 // Copyright (c) 2014年 apple. A

统计01矩阵中全1子矩阵的个数

统计01矩阵中全1子矩阵的个数 1.51Nod 1291 题意:600*600的01矩阵,统计宽i高j的全1矩阵的个数. 题解:枚举矩阵的下边界,对于每个下边界,统计所有宽极大的矩形的答案(高度可以用差分).\(n^2\) 统计完之后,我们已知所有高度的宽极大的答案,列一下式子发现两次前缀和就是最后答案. 代码: #include<bits/stdc++.h> using namespace std; #define fi first #define se second #define mp

[EOJ Monthly 2018.10][C. 痛苦的 01 矩阵]

题目链接:C. 痛苦的 01 矩阵 题目大意:原题说的很清楚了,不需要简化_(:з」∠)_ 题解:设\(r_i\)为第\(i\)行中0的个数,\(c_j\)为第\(j\)列中0的个数,\(f_{i,j}\)代表对应格子是否为0,则有\(cost(i,j)=r_i+c_j-f_{i,j}\),\((cost(i,j))^2=r_i^2+c_j^2+f_{i,j}+2r_ic_j-2f_{i,j}(r_i+c_j)\) $$\sum_{i=1}^n \sum_{j=1}^n \left( cost(