LeetCode 1162 地图分析 最短路径
1. 题目
你现在手里有一份大小为 n x n 的 网格 grid,上面的每个 单元格 都用 0 和 1 标记好了。其中 0 代表海洋,1 代表陆地,请你找出一个海洋单元格,这个海洋单元格到离它最近的陆地单元格的距离是最大的。如果网格上只有陆地或者海洋,请返回 -1。
我们这里说的距离是「曼哈顿距离」( Manhattan Distance):(x0, y0) 和 (x1, y1) 这两个单元格之间的距离是 |x0 - x1| + |y0 - y1|
2. 思路
- 单源最短路径
本体可以视为每个海洋单元离陆地的最短路径的最大值,res=max(res,bfs(海洋单元格))
最短路径:使用广度优先遍历算法,遍历图时,和遍历树的思路基本一致,将该时刻所能到达的点(树中为该节点的子节点,该题中为该点上下左右四个点)加入队列中,然后在下一个时刻从队列中取出,因为每个时间点只能走一步,在图中的表现每个点每次只会向周围点扩散一次,所以当队列最后为空时,则视为遍历完整个图,因为树时单向的,图时无向图,因此遍历时,需要记录该点是否被访问过,如果被访问直接跳过。
时间复杂度为O(n^4) - 多源最短路径
因为该题中有多个海洋点,因此我们视为从每个陆地出发,寻找最远的海洋点。因此,我们将陆地视为源点,依次遍历陆地周围的所有海洋点,最后遍历到的海洋就时最远的海洋点。多源最短路径可以视为单源最短的第二层,也就是有一个超级源点的单源最短路径。
3. Code
1.单源最短路径
from queue import Queue
class Solution:
def maxDistance(self, grid: List[List[int]]) -> int:
n=len(grid)
res=-1
for i in range(n):
for j in range(n):
#遍历每个海洋点
if grid[i][j]==0:
res=max(res,self.bfs(i,j,n,grid))
return res
def bfs(self,x,y,n,grid):
#四个方向
dir=[[-1,0],[1,0],[0,-1],[0,1]]
deq=Queue()
#记录每个点走的步数
m={}
deq.put([x,y])
m[x*n+y]=0
while not deq.empty():
cur=deq.get()
dx=cur[0]
dy=cur[1]
step=m[dx*n+dy]
#到达陆地点,直接返回步数
if grid[dx][dy]==1:
return step
#遍历四个方向
for di in dir:
nx=dx+di[0]
ny=dy+di[1]
#超出边界
if nx<0 or nx>=n or ny<0 or ny>=n:
continue
key=nx*n+ny
#已经访问
if key in m.keys():
continue
deq.put([nx,ny])
m[key]=step+1
return -1
- 多源最短路径
from queue import Queue
class Solution:
def maxDistance(self, grid: List[List[int]]) -> int:
dir=[[-1,0],[1,0],[0,-1],[0,1]]
n=len(grid)
deq=Queue()
m={}
#将所有陆地放入队列,从每个陆地出发,求最远的海洋
for i in range(n):
for j in range(n):
if grid[i][j]==1:
deq.put([i,j])
m[n*i+j]=0
res=-1
while not deq.empty():
cur=deq.get()
dx=cur[0]
dy=cur[1]
step=m[n*dx+dy]
for di in dir:
nx=dx+di[0]
ny=dy+di[1]
if nx<0 or nx>=n or ny<0 or ny>=n:
continue
#跳过陆地点
if grid[nx][ny]!=0:
continue
grid[nx][ny]=step+1
deq.put([nx,ny])
m[nx*n+ny]=step+1
#因为每一步都是在走海洋点的路径上,所以当前路径就是一条合法路径
res=max(res,step+1)
return res