题目描述
给定一个由 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。
矩阵中的元素只在四个方向上相邻: 上、下、左、右。
思路分析(仔细看)
广度优先搜索一:
public int[][] updateMatrix(int[][] matrix){
//首先将所有的0都入队,并且将1的位置设置成-1,表示该位置是未被访问过的1
Queue<int[]> queue=new LinkedList<int[]>();
int m=matrix.length,n=matrix[0].length;
for (int i = 0; i <m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j]==0) {
//add()和offer()两者都是往队列尾部插入元素,不同的时候,当超出队列界限的时候,
//add()方法是抛出异常让你处理,而offer()方法是直接返回false
queue.offer(new int[] {i,j});
}else {
matrix[i][j]=-1;
//将数组中的1改为-1(或者Integer.MAX_VALUE啦,1000啦,m*n都行)
//只要是个无效的距离值来标志这个位置的 1 没有被访问过就行辣~
}
}
}
//左右下上
int[] dx=new int[] {-1,1,0,0};
int[] dy=new int[] {0,0,-1,1};
while (!queue.isEmpty()) {
int[] point=queue.poll();
int x=point[0],y=point[1];//获取到0的x和y坐标
for (int i = 0; i < 4; i++) {
int newX=x+dx[i];
int newY=y+dy[i];
//如果四领域的点是-1,表示这个点是未被访问过的1
//所以这个点到0的距离就可以更新成为matrix[x][y]+1
if (newX>=0&&newX<m&&newY>=0&&newY<n&&matrix[newX][newY]==-1) {
matrix[newX][newY]=matrix[x][y]+1;
queue.offer(new int[] {newX,newY});
}
}
}
return matrix;
}
广度优先搜索二:
另一种 BFS 思路:
本题还有一种 BFS 的做法,就是先找出在 0边上的所有的 1,然后把这些 1放到队列里,后续BFS的时候就只关心 1的值。
public int[][] updateMatrix2(int[][] matrix) {
// 首先将0边上的1入队
int[] dx = new int[] { -1, 1, 0, 0 };
int[] dy = new int[] { 0, 0, -1, 1 };
Queue<int[]> queue = new LinkedList<int[]>();
int m = matrix.length, n = matrix[0].length;
int[][] res = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == 0) {
for (int k = 0; k < 4; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] == 1 && res[x][y] == 0) {
// 这是在 0 边上的1。需要加上 res[x][y] == 0 的判断防止重复入队
res[x][y] = 1;
queue.offer(new int[] { x, y });// 把1的位置放入队列
}
}
}
}
}
while (!queue.isEmpty()) {
int[]point=queue.poll();
int x=point[0],y=point[1];
for (int i = 0; i < 4; i++) {
int newX=x+dx[i];
int newY=y+dy[i];
if (newX>=0&&newX<m&&newY>=0&&newY<n&&matrix[newX][newY]==1&&res[newX][newY]==0) {
res[newX][newY]=res[x][y]+1;
queue.offer(new int[] {newX,newY});
}
}
}
return res;
}
动态规划方法
class Solution {
public int[][] updateMatrix(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
int[][] dp = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
dp[i][j] = matrix[i][j] == 0 ? 0 : 10000;
}
}
// 从左上角开始
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (i - 1 >= 0) {
dp[i][j] = Math.min(dp[i][j], dp[i - 1][j] + 1);
}
if (j - 1 >= 0) {
dp[i][j] = Math.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] = Math.min(dp[i][j], dp[i + 1][j] + 1);
}
if (j + 1 < n) {
dp[i][j] = Math.min(dp[i][j], dp[i][j + 1] + 1);
}
}
}
return dp;
}
}
总结(必看知识点)
深度遍历:
第一步:明确递归参数
第二步:明确递归终止条件
第三步:明确递归函数中的内容
第四步:明确回溯返回值
广度遍历:
第一步:设置队列,添加初始节点
第二步:判断队列是否为空
第三步:迭代操作 弹出队列元素,进行逻辑处理 当前队列元素的下级元素,入队
第四步:在此执行步骤三
队列的方法
add 增加一个元 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
offer 添加一个元素并返回true 如果队列已满,则返回false
poll 移除并返问队列头部的元素 如果队列为空,则返回null
peek 返回队列头部的元素 如果队列为空,则返回null
put 添加一个元素 如果队列满,则阻塞
take 移除并返回队列头部的元素 如果队列为空,则阻塞