1091. 二进制矩阵中的最短路径

79 篇文章 3 订阅

1091. 二进制矩阵中的最短路径

给你一个 n x n 的二进制矩阵 grid 中,返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径,返回 -1 。

二进制矩阵中的 畅通路径是一条从 左上角 单元格(即,(0, 0))到 右下角 单元格(即,(n - 1, n - 1))的路径,该路径同时满足下述要求:

  • 路径途经的所有单元格都的值都是 0 。
  • 路径中所有相邻的单元格应当在 8 个方向之一 上连通(即,相邻两单元之间彼此不同且共享一条边或者一个角)。

畅通路径的长度 是该路径途经的单元格总数。

示例 1:

在这里插入图片描述

输入:grid = [[0,1],[1,0]]
输出:2

示例2:

在这里插入图片描述

输入:grid = [[0,0,0],[1,1,0],[1,1,0]]
输出:4

示例3:

输入:grid = [[1,0,0],[1,1,0],[1,1,0]]
输出:-1

提示:
  • n == grid.length
  • n == grid[i].length
  • 1 <= n <= 100
  • grid[i][j] 为 0 或 1

思路:(BFS)

典型的BFS最短路径问题,用DFS也可以求解,但是容易超时。

在二维矩阵中搜索,什么时候用BFS,什么时候用DFS?

1.如果只是要找到某一个结果是否存在,那么 DFS 会更高效。因为DFS会首先把一种可能的情况尝试到底,才会回溯去尝试下一种情况,只要找到一种情况,就可以返回了。但是BFS必须所有可能的情况同时尝试,在找到一种满足条件的结果的同时,也尝试了很多不必要的路径;
2.如果是要找所有可能结果中最短的,那么 BFS 回更高效。因为DFS是一种一种的尝试,在把所有可能情况尝试完之前,无法确定哪个是最短,所以DFS必须把所有情况都找一遍,才能确定最终答案(DFS的优化就是剪枝,不剪枝很容易超时)。而BFS从一开始就是尝试所有情况,所以只要找到第一个达到的那个点,那就是最短的路径,可以直接返回了,其他情况都可以省略了,所以这种情况下,BFS更高效。

BFS解法中的visited为什么可以全局使用?

BFS是在尝试所有的可能路径,哪个最快到达终点,哪个就是最短。

那么每一条路径走过的路不同,visited(也就是这条路径上走过的点)也应该不同,那么为什么visited可以全局使用呢?
因为我们要找的是最短路径,那么如果在此之前某个点已经在visited中,也就是说有其他路径在小于或等于当前步数的情况下,到达过这个点,证明到达这个点的最短路径已经被找到。那么显然这个点没必要再尝试了,因为即便去尝试了,最终的结果也不会是最短路径了,所以直接放弃这个点即可。

在程序实现 BFS 时需要考虑以下问题:
  • 队列:用来存储每一轮遍历得到的节点;
  • 标记:对于遍历过的节点,应该将它标记,防止重复遍历

代码:(Java)

import java.util.LinkedList;
import java.util.Queue;

public class bfs_bi_lesros {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		int [][]grid = {{0,0,0},{1,1,0},{1,1,0}};
		System.out.println(shortestPathBinaryMatrix(grid));
	}
	public static int shortestPathBinaryMatrix(int[][] grid) {
		
		//过滤掉特殊情况
		if(grid == null || grid.length ==0 || grid[0].length == 0) {
			return -1;
		}
		//如果起点就阻塞肯定隔P
		if(grid[0][0] == 1) {
			return -1;
		}
		//可以走的8 个方向
		int [][] dir= {{-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}};
		int m = grid.length;//行
		int n = grid[0].length;//列
		
		if(m != n)
			return -1;
		
		//bfs的老套路 上队列
		Queue<int[]> queue = new LinkedList<int[]>(); //LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用
		//将起点丢进去
		queue.add(new int[]{0, 0});
		
		grid[0][0] = 1;//把起点设为阻塞,走过得直接阻塞
		
		int path = 1;//层数
		
		while(!queue.isEmpty()) {
			int size = queue.size();//每一层的个数
			while(size-- > 0) {
				int [] cur = queue.poll();
				int x = cur[0];
				int y = cur[1];
				
				//如果等于终点则返回
				if(x == m-1 && y == n-1) {
					return path;
				}
				
				//开始8个方向判断
				for(int [] d : dir) {
					int x1 = x + d[0];
					int y1 = y + d[1];
					
					//过滤越界和阻塞的情况
					if(x1 < 0 || x1 >= m || y1 < 0 || y1 >= m || grid[x1][y1] == 1) {
						continue;
					}
					
					//把在数组范围内,并且可以走的放入队列
					queue.add(new int [] {x1, y1});
					grid[x1][y1] = 1; //已走,设为阻塞
				}
			}
			path++; //遍历完一层, 路径加1
		}
		
		return -1;
    }
}
输出:

在这里插入图片描述

注:仅供学习参考

题目来源:力扣

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷酷的懒虫

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值