给定一个由 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的点都存入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