文章目录
- 多源最短路径问题介绍
- [一、01 矩阵](#一、01 矩阵)
- 二、飞地的数量
- 三、地图中的最高点
- 四、地图分析
多源最短路径问题介绍
- 上一篇内容的单源最短路径问题的解法:把起点加入到队列 --->一层一层地往外扩展
- 多源最短路径问题,顾名思义,就是在单源最短路径问题的一个起点的基础上变为了有多个起点,寻找其中到终点路程最短的那个路径。
如何解决?
解法一:
- 把多源最短路问题转化成若干个单源最短路问题,找出针对于每个起点的最短路径,最后对比一下找出这些路径中最短的那个。
- 时间复杂度太高,大概率会超时
解法二:
- 多源BFS算法
- 把所有的起点合并为一个起点,这样就变为了单源最短路径问题。
- 具体代码实现方法就是先将所有的起点加入到队列里面,然后还是继续一层一层地往外扩展(
其实也就是相当于以这样的方式实现多个起点同时进行BFS,还是最短的那个路径会最率先触碰到终点)
看图复习思路:

一、01 矩阵
给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。
两个相邻元素间的距离为 1 。

解题思路
多源BFS+正难则反:
-
0元素不用管,到它到最近的 0 的距离就是自己,距离为0。
我们要针对于每个元素1进行处理,也就是对每个1都要进行BFS操作,那么对于这样的问题我们就要意识到这就是多源BFS问题。 -
正难则反:我觉得在本题中是这样应用的,我们要对于每个1使用BFS找到它到最近的 0 的距离,但是我们又想要定位到1的坐标,以便在distance矩阵中对应位置标记。而想要定位到谁的坐标就不能以谁为起点进行BFS,所以我们反过来以所有的0为起点进行多源BFS,定位到每个1,就这样改为去查找距离0最近的1(对于每个1来说肯定是离它最近的0先定位到它)。
-
对比上一篇文章中的单源BFS问题,在本题中我们就不在需要一个step变量来记录所走过的步数了,根据我们在BFS中对下一步进行扩展的代码实现方式,我们可以发现:因为到了下一个位置(x=a+dx,y=b+dy)需要的步数其实就是distance矩阵中的上一个位置(a,b)中储存的值+1。那么单源BFS问题中我们需要严格按照一层一层地顺序来处理坐标,这是为了step的记录(每扩展一层,step++),那既然此时我们连step都不需要了,也就不需要再分层进行BFS了
代码实现及解析
java
class Solution {
public int[][] updateMatrix(int[][] mat) {
int m=mat.length,n=mat[0].length;
int[][] distance=new int[m][n];//记录距离(输出矩阵)
for(int i=0;i<m;i++){//先把distance矩阵的所有位置标记为-1,这样就可以使用distance矩阵来防止重复处理,处理过的位置在distance矩阵中的值就不会是-1
for(int j=0;j<n;j++){
distance[i][j]=-1;
}
}
Queue<int[]> que=new LinkedList<>();
//先遍历一下原矩阵mat
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(mat[i][j]==0){//0元素不用特殊处理,离自己的距离为0
distance[i][j]=0;
que.offer(new int[]{i,j});//并把0元素的坐标入队列
}
}
}
//偏移量数组:
int[] dx=new int[]{0,0,1,-1};
int[] dy=new int[]{1,-1,0,0};
//开始BFS的过程(以所有的0元素为起点向外扩展到达元素1):
while(!que.isEmpty()){
int[] tmp=que.poll();
int a=tmp[0],b=tmp[1];
for(int i=0;i<4;i++){//向(a,b)的上下左右进行扩展
int x=a+dx[i],y=b+dy[i];
if(x>=0&&x<m&&y>=0&&y<n&&mat[x][y]==1&&distance[x][y]==-1){//找到1,并且该元素1并没有被处理过(distance标记为-1)
que.offer(new int[]{x,y});
distance[x][y]=distance[a][b]+1;//x、y是从(a,b)位置扩展来的,所以(x,y)的步数可以由distance矩阵中(a,b)得来
}
}
}
return distance;
}
}
总结
复习解题思路和代码注释
二、飞地的数量
给你一个大小为 m x n 的二进制矩阵 grid ,其中 0 表示一个海洋单元格、1 表示一个陆地单元格。
一次 移动 是指从一个陆地单元格走到另一个相邻(上、下、左、右)的陆地单元格或跨过 grid 的边界。
返回网格中 无法 在任意次数的移动中离开网格边界的陆地单元格的数量。

解题思路
- 与专题十五中一题:【被围绕的区域】非常相似,此题的解题方法也和该题一样。
- 正难则反:先处理接触边缘的区域(将其每块位置标记),然后当我们再完整地遍历一遍矩阵时,剩下的"1"区域就一定是不接触边缘的,此时就可以计数。
- 但是之前是:遍历边缘找到目标位置(元素1)--->对其使用BFS找到其连接的符合条件的区域,对它们全部进行标记(对每个目标位置一个一个地进行BFS),这次我们可以采用多源BFS的方法完成这一步:将边缘上的目标位置(元素1)先全部入队列--->然后对它们同时进行BFS操作(多源BFS),这样完成对其连接的区域的标记操作。
- 熟悉多源BFS在本题的这种应用方式
三、地图中的最高点
给你一个大小为 m x n 的整数矩阵 isWater ,它代表了一个由 陆地 和 水域 单元格组成的地图。
如果 isWater[i][j] = = 0 ,格子 (i, j) 是一个 陆地 格子。
如果 isWater[i][j] = = 1 ,格子 (i, j) 是一个 水域 格子。
你需要按照如下规则给每个单元格安排高度:
每个格子的高度都必须是非负的。
如果一个格子是 水域 ,那么它的高度必须为 0 。
任意相邻的格子高度差 至多 为 1 。当两个格子在正东、南、西、北方向上相互紧挨着,就称它们为相邻的格子。(也就是说它们有一条公共边)
找到一种安排高度的方案,使得矩阵中的最高高度值 最大 。
请你返回一个大小为 m x n 的整数矩阵 height ,其中 height[i][j] 是格子 (i, j) 的高度。如果有多种解法,请返回 任意一个 。

可以看出本题与上面第一题【01 矩阵】相同。
解题思路
多源BFS:
- 与【01 矩阵】类似,先将水区域的高度全部记录为0,再以所有水区域为起点,同时进行BFS,每向四周扩展一步,就将扩展出的位置的高度+1(这样才符合题意,相邻高度差不得超过1),最终扩展出每一步之后,输出矩阵中就存着最大高度了
代码实现及解析
java
class Solution {
int[] dx=new int[]{0,0,1,-1};
int[] dy=new int[]{1,-1,0,0};
public int[][] highestPeak(int[][] isWater) {
int m=isWater.length,n=isWater[0].length;
int[][] hightMap=new int[m][n];
for(int i=0;i<m;i++){//先把hightMap全部初始化为-1
for(int j=0;j<n;j++){
hightMap[i][j]=-1;
}
}
Queue<int[]> que=new LinkedList<>();
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(isWater[i][j]==1){//将所有水区域的高度均记为0
hightMap[i][j]=0;
que.offer(new int[]{i,j});//并将这些位置入队列
}
}
}
//多源BFS:
while(!que.isEmpty()){
int[] tmp=que.poll();//以所有水区域为起点,进行BFS,每向四周扩展一步,就将扩展出的位置的高度+1
int a=tmp[0],b=tmp[1];
for(int i=0;i<4;i++){
int x=a+dx[i],y=b+dy[i];
if(x>=0&&x<m&&y>=0&&y<n&&isWater[x][y]==0&&hightMap[x][y]==-1){
hightMap[x][y]=hightMap[a][b]+1;//将扩展出的位置的高度+1
que.offer(new int[]{x,y});//并将该位置继续入队列,以便下一次继续扩展
}
}
}
return hightMap;
}
}
总结
复习解题思路想出用多源BFS来解题后,本题也就不难了,但是要想明白我们是如何分析本题以及第一题【01矩阵】的题意后得出使用多源BFS算法的
四、地图分析
你现在手里有一份大小为 n x n 的 网格 grid,上面的每个 单元格 都用 0 和 1 标记好了。其中 0 代表海洋,1 代表陆地。
请你找出一个海洋单元格,这个海洋单元格到离它最近的陆地单元格的距离是最大的,并返回该距离。如果网格上只有陆地或者海洋,请返回 -1。
我们这里说的距离是「曼哈顿距离」( Manhattan Distance):(x0, y0) 和 (x1, y1) 这两个单元格之间的距离是 |x0 - x1| + |y0 - y1| 。(其实就是最短路径)
解题思路
- 直接按我们写了几遍了的多源BFS+正难则反的解法来做确实不难,而且与前面的几题非常相似,但是关键在于我们如何读懂题意,如何结合题意将其抽象为多源BFS模型的,如何处理好每题所存在的细节问题的。
- 我们是要针对于每个海洋单元格,找出离它最近的陆地单元格的距离,要注意我们是要定位到具体的每个海洋单元格的位置的,得出它们每个的最短路径,对比找出最大的那个,所以就像我们上面强调的那样,就不能使用海洋单元格为起点了(正难则反思想),我们要以所有的陆地单元格为起点进行扩展。
代码实现及解
java
class Solution {
public int maxDistance(int[][] grid) {
int[] dx=new int[]{0,0,1,-1};
int[] dy=new int[]{1,-1,0,0};
int m=grid.length,n=grid[0].length;
int[][] distance=new int[m][n];//方便记录每次扩展后的步数,还可以以这个矩阵当做访问状态记录矩阵
//初始化distance矩阵
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
distance[i][j]=-1;
}
}
Queue<int[]> que=new LinkedList<>();
for(int i=0;i<m;i++){//将陆地入队列(从所有的陆地为起点开始扩展),并将其在distance矩阵中的值初始化为0
for(int j=0;j<n;j++){
if(grid[i][j]==1){
distance[i][j]=0;
que.offer(new int[]{i,j});
}
}
}
int ret=-1;//记录最大距离
//多源BFS:
while(!que.isEmpty()){
int[] tmp=que.poll();
int a=tmp[0],b=tmp[1];
for(int i=0;i<4;i++){
int x=a+dx[i],y=b+dy[i];
if(x>=0&&x<m&&y>=0&&y<n&&distance[x][y]==-1){
distance[x][y]=distance[a][b]+1;
ret=Math.max(ret,distance[x][y]);
que.offer(new int[]{x,y});
}
}
}
return ret;
}
}
总结
复习解题思路可以看到题目并未要求我们返回一个新矩阵记录所有的计算结果,但是在代码的开头我们仍使用了这样的矩阵,这是因为这个矩阵有两点很好的用处:1. 方便记录每次扩展后的步数(int x=a+dx[i],y=b+dy[i]; distance[x][y]=distance[a][b]+1) 2.还可以以这个矩阵当做位置访问状态记录矩阵(将矩阵的值初始化为-1),所以必须要用代码中我们使用的是正常的多源BFS实现方式:每扩展一个单元格就记录其距离,并判断是否为这些最短路径中最大的。但其实即使是多个起点进行BFS也一样,在本题的要求条件下我们也可以像单源BFS那样一层一层地扩展,每扩展一层step就+1,这样有时也许更符合我们的思路