单源点系列:
一、二进制矩阵中的最短路径
给你一个 n x n
的二进制矩阵 grid
中,返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径,返回 -1
。
二进制矩阵中的 畅通路径 是一条从 左上角 单元格(即,(0, 0)
)到 右下角 单元格(即,(n - 1, n - 1)
)的路径,该路径同时满足下述要求:
- 路径途经的所有单元格的值都是
0
。 - 路径中所有相邻的单元格应当在 8 个方向之一 上连通(即,相邻两单元之间彼此不同且共享一条边或者一个角)。
思路:
由题意可得,bfs是从(0,0)出发(单源点),然后找到一条最短畅通路径的长度。
1.首先我们要判断起点和终点的值是否都是0,如果不是直接返回-1;
2.然后判断矩阵的规格,如果是1*1的矩阵,直接返回1;
3.然后开始bfs搜索,每次我们可以朝着八个方向走出一步,所以定义一个方向数组。
java
int[][] directions={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
4.然后套用dfs的模板即可。
java
while(!queue.isEmpty()){
int size=queue.size();
for(int i=0;i<size;i++){
int[] next=queue.poll();//循环遍历这一层的元素
for(int[] direc:directions){
int newX=next[0]+direc[0];
int newY=next[1]+direc[1];
if(newX>=0&&newX<m&&newY>=0&&newY<n&&!visited[newX][newY]&&grid[newX][newY]==0){
if(newX==m-1&&newY==n-1)return step+1;
visited[newX][newY]=true;
queue.add(new int[]{newX,newY});
}
}
}
step++;
}
代码:
java
class Solution {
boolean[][] visited;
int m;
int n;
int[][] directions={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
public int shortestPathBinaryMatrix(int[][] grid) {
if(grid[0][0]!=0||grid[grid.length-1][grid[0].length-1]!=0)return -1;
m=grid.length;
n=grid[0].length;
if(m == 1 && n == 1 && grid[0][0] == 0) return 1;
visited=new boolean[m][n];
Queue<int[]> queue=new ArrayDeque<int[]>();
queue.add(new int[]{0,0});
visited[0][0]=true;
int step=1;
while(!queue.isEmpty()){
int size=queue.size();
for(int i=0;i<size;i++){
int[] next=queue.poll();//循环遍历这一层的元素
for(int[] direc:directions){
int newX=next[0]+direc[0];
int newY=next[1]+direc[1];
if(newX>=0&&newX<m&&newY>=0&&newY<n&&!visited[newX][newY]&&grid[newX][newY]==0){
if(newX==m-1&&newY==n-1)return step+1;
visited[newX][newY]=true;
queue.add(new int[]{newX,newY});
}
}
}
step++;
}
return -1;
}
}
二、迷宫中离入口最近的出口
给你一个 m x n
的迷宫矩阵 maze
(下标从 0 开始 ),矩阵中有空格子(用 '.'
表示)和墙(用 '+'
表示)。1.同时给你迷宫的入口 entrance
,用 entrance = [entrancerow, entrancecol]
表示你一开始所在格子的行和列。
每一步操作,2.你可以往 上,下,左 或者 右 移动一个格子 。你不能进入墙所在的格子,你也不能离开迷宫。3.你的目标是找到离 entrance
最近 的出口 。出口 的含义是 maze
边界 上的 空格子 。entrance
格子 不算 出口。
请你返回从 entrance
到最近出口的最短路径的 步数 ,如果不存在这样的路径,请你返回 -1
。
思路:
1.首先,这是一个单源点的bfs搜索题,for循环不断遍历每一次搜索能到达的点,然后把这些点加入queue队列中。
2.然后再次for循环遍历这些队列,到达出口的判定条件是:在第一行或者最后一行 第一列或者最后一列。
代码:
java
class Solution {
int[][] directions = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
public int nearestExit(char[][] maze, int[] entrance) {
int m = maze.length;
int n = maze[0].length;
boolean[][] visited = new boolean[m][n];
Queue<int[]> queue = new ArrayDeque<int[]>();
queue.add(entrance);
visited[entrance[0]][entrance[1]] = true;
int steps = 0;
while (!queue.isEmpty()) {
int size=queue.size();
for (int i = 0; i < size; i++) {
int[] current = queue.poll();
for (int[] dir : directions) {
int newX = current[0] + dir[0];
int newY = current[1] + dir[1];
if (newX >= 0 && newX < m && newY >= 0 && newY < n && !visited[newX][newY] && maze[newX][newY] == '.') {
if (newX == 0 || newX == m - 1 || newY == 0 || newY == n - 1)
return steps+1;
visited[newX][newY] = true;
queue.add(new int[] { newX, newY });
}
}
}
steps++;
}
return -1;
}
}
多源点系列:
三、地图分析
你现在手里有一份大小为 n x n
的 网格 grid
,上面的每个 单元格 都用 0
和 1
标记好了。其中 0
代表海洋,1
代表陆地。
请你找出一个海洋单元格,这个海洋单元格到离它最近的陆地单元格的距离是最大 的,并返回该距离。如果网格上只有陆地或者海洋,请返回 -1
。
我们这里说的距离是「曼哈顿距离」( Manhattan Distance):(x0, y0)
和 (x1, y1)
这两个单元格之间的距离是 |x0 - x1| + |y0 - y1|
。
思路:
要求0到最近的1的距离的最大值。我们可以计算1到最近的0的距离的最大值。
多源点问题:
1.首先我们将所有值为1的点都加入到队列中,然后将值为0的点的值都改为-1;(这样就不用使用visited数组来记忆化了)
2.然后对队列循环,当遇到-1的时候,我们将它标记一下。然后继续寻找。最后返回step-1
(为什么是step-1呢,因为在最后一次查找的时候,已经无法再向外层扩展了,但还是++,所以最后返回的时候应该再减去1)
代码:
java
class Solution {
int[][] directions = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
int[][] grid;
int m;
int n;
public int maxDistance(int[][] grid) {
this.grid = grid;
m = grid.length;
n = grid[0].length;
Queue<int[]> queue = new ArrayDeque<int[]>();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
queue.add(new int[] { i, j });
}else{
grid[i][j]=-1;
}
}
}
if(queue.size()==0||queue.size()==n*n)return -1;
int step=0;
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
int[] current = queue.poll();
for (int[] dirc : directions) {
int newX = current[0] + dirc[0];
int newY = current[1] + dirc[1];
if (newX >= 0 && newX < m && newY >= 0 && newY < n &&grid[newX][newY]==-1) {
grid[newX][newY]=step+1;
queue.add(new int[] { newX, newY });
}
}
}
step++;
}
return step-1;
}
}
四、01矩阵
给定一个由 0
和 1
组成的矩阵 mat
,请输出一个大小相同的矩阵,其中每一个格子是 mat
中对应位置元素到最近的 0
的距离。
两个相邻元素间的距离为 1
。
示例 1:
输入:mat = [[0,0,0],[0,1,0],[0,0,0]]
输出:[[0,0,0],[0,1,0],[0,0,0]]
思路:
题目中要求每一个格子距离0的最小值。我们还是逆向思维,以1为主,找距离最小的0。
多源点问题:1.首先将所有的值为1的点都加入队列中,然后将值为0的点赋值为不能达到的值
2.然后套用模板,开始遍历队列。如果遇到值==-1的点,那我们就将这个格子的值赋为step;
3.最后返回格子即可。(我们在格子上原地修改)
代码:
java
class Solution {
public int[][] updateMatrix(int[][] mat) {
int[][] directions={{0,-1},{0,1},{-1,0},{1,0}};
int m=mat.length;
int n=mat[0].length;
boolean[][] visited=new boolean[m][n];
Queue<int[]> queue=new ArrayDeque<int[]>();
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(mat[i][j]==0)queue.add(new int[]{i,j});
else mat[i][j]=-1;//先将值为1的位置赋为-1
}
}
int step=1;
while(!queue.isEmpty()){
int size=queue.size();
for(int i=0;i<size;i++){
int[] current=queue.poll();
for(int[] dirc:directions){
int newX=current[0]+dirc[0];
int newY=current[1]+dirc[1];
if(newX>=0&&newX<m&&newY>=0&&newY<n&&!visited[newX][newY]&&mat[newX][newY]==-1){
mat[newX][newY]=step;
queue.add(new int[]{newX,newY});
}
}
}
step++;
}
return mat;
}
}