写在前面
第一次特别写一下leetcode的打卡题,因为今天的打卡题还挺不错的,练习了bfs和简单的dp。
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
注意:
给定矩阵的元素个数不超过 10000。
给定矩阵中至少有一个元素是 0。
矩阵中的元素只在四个方向上相邻: 上、下、左、右。
BFS
有两种思路:
- 找到1之后,去找0,然后算出最小的距离。
- 找到0,开始扩散,算出所有1到这个0的距离,记录最小值。
两种思路都可以算出答案,但是第一种方法从复杂度上来看明显有点高,所以这里我们采用第二种思路。
假设有这样一个矩阵
_ _ _ _
_ 0 _ _
_ _ _ _
_ _ _ _
只有一个0,我们从这个开始广度优先搜索,可以得到以下结果:
然后我们再维护一个queue,一开始把所有0的xy坐标放进去,然后bfs的过程中,节点距离更新的坐标也放进去,然后bfs就行了。
代码:
class Solution {
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
if(matrix.empty()) return matrix;
int m = matrix.size();
int n = matrix[0].size();
vector<vector<int>> res(m,vector<int>(n,INT_MAX-1));
vector<pair<int,int>> dir = {{0,1},{0,-1},{1,0},{-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){//找0
res[i][j] = 0;
q.push({i,j});
}
}
while(!q.empty()){
auto tmp = q.front();
q.pop();
for(int i=0; i<4; ++i){
int x = tmp.first + dir[i].first;
int y = tmp.second + dir[i].second;
if(x>=0 && y>=0 && x<m && y<n && res[x][y]>res[tmp.first][tmp.second]+1){//BFS
res[x][y] = res[tmp.first][tmp.second] + 1;
q.push({x,y});
}
}
}
return res;
}
};
dp
这道题用dp其实比BFS要简介非常多,不管是思路还是代码,回到上一个解法的思路1,我们从一个1开始找0的话,有四种方向:
- 只有 水平向左移动 和 竖直向上移动;
- 只有 水平向左移动 和 竖直向下移动;
- 只有 水平向右移动 和 竖直向上移动;
- 只有 水平向右移动 和 竖直向下移动。
然后其实上一个解法的BFS里面,已经包含了我们需要的转移方程:
res[x][y]>res[tmp.first][tmp.second]+1
换成dp的话,转移方程就是这样的:
然后分别计算4个方向就行了。
扫描二维码关注公众号,回复:
11149842 查看本文章
代码:
class Solution {
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
vector<vector<int>> dp(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) {
dp[i][j] = 0;
}
}
}
// 只有 水平向左移动 和 竖直向上移动
for (int i=0; i<m; ++i) {
for (int j=0; j<n; ++j) {
if (i-1 >= 0) {
dp[i][j] = min(dp[i][j], dp[i-1][j]+1);
}
if (j-1 >= 0) {
dp[i][j] = min(dp[i][j], dp[i][j-1]+1);
}
}
}
// 只有 水平向左移动 和 竖直向下移动
for (int i=m-1; i>=0; --i) {
for (int j=0; j<n; ++j) {
if (i+1<m) {
dp[i][j] = min(dp[i][j],dp[i+1][j]+1);
}
if (j-1>=0) {
dp[i][j] = min(dp[i][j],dp[i][j-1]+1);
}
}
}
// 只有 水平向右移动 和 竖直向上移动
for (int i=0; i<m; ++i) {
for (int j=n-1; j>=0; --j) {
if (i-1>=0) {
dp[i][j] = min(dp[i][j],dp[i-1][j]+1);
}
if (j+1<n) {
dp[i][j] = min(dp[i][j],dp[i][j+1]+1);
}
}
}
// 只有 水平向右移动 和 竖直向下移动
for (int i=m-1; i>=0; --i) {
for (int j=n-1; j>=0; --j) {
if (i+1<m) {
dp[i][j] = min(dp[i][j],dp[i+1][j]+1);
}
if (j+1<n) {
dp[i][j] = min(dp[i][j],dp[i][j+1]+1);
}
}
}
return dp;
}
};
dp小优化
其实通过手动模拟dp过程可以发现,没必要算四个方向,只需要两个方向就够了:
-
只有 水平向左移动 和 竖直向上移动;
-
只有 水平向右移动 和 竖直向下移动。
代码:
class Solution {
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
vector<vector<int>> dp(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) {
dp[i][j] = 0;
}
}
}
// 只有 水平向左移动 和 竖直向上移动
for (int i=0; i<m; ++i) {
for (int j=0; j<n; ++j) {
if (i-1 >= 0) {
dp[i][j] = min(dp[i][j], dp[i-1][j]+1);
}
if (j-1 >= 0) {
dp[i][j] = min(dp[i][j], dp[i][j-1]+1);
}
}
}
// 只有 水平向右移动 和 竖直向下移动
for (int i=m-1; i>=0; --i) {
for (int j=n-1; j>=0; --j) {
if (i+1<m) {
dp[i][j] = min(dp[i][j],dp[i+1][j]+1);
}
if (j+1<n) {
dp[i][j] = min(dp[i][j],dp[i][j+1]+1);
}
}
}
return dp;
}
};