1. 问题描述:
给你一个 m x n 的网格 grid。网格里的每个单元都代表一条街道。grid[i][j] 的街道可以是:
1 表示连接左单元格和右单元格的街道。
2 表示连接上单元格和下单元格的街道。
3 表示连接左单元格和下单元格的街道。
4 表示连接右单元格和下单元格的街道。
5 表示连接左单元格和上单元格的街道。
6 表示连接右单元格和上单元格的街道。
你最开始从左上角的单元格 (0,0) 开始出发,网格中的「有效路径」是指从左上方的单元格 (0,0) 开始、一直到右下方的 (m-1,n-1) 结束的路径。该路径必须只沿着街道走。
注意:你 不能 变更街道。
如果网格中存在有效的路径,则返回 true,否则返回 false
示例 1:
输入:grid = [[2,4,3],[6,5,2]]
输出:true
解释:如图所示,你可以从 (0, 0) 开始,访问网格中的所有单元格并到达 (m - 1, n - 1)
示例 2:
输入:grid = [[1,2,1],[1,2,1]]
输出:false
解释:如图所示,单元格 (0, 0) 上的街道没有与任何其他单元格上的街道相连,你只会停在 (0, 0) 处。
示例 3:
输入:grid = [[1,1,2]]
输出:false
解释:你会停在 (0, 1),而且无法到达 (0, 2) 。
示例 4:
输入:grid = [[1,1,1,1,1,1,3]]
输出:true
示例 5:
输入:grid = [[2],[2],[2],[2],[2],[2],[6]]
输出:true
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
1 <= grid[i][j] <= 6
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/check-if-there-is-a-valid-path-in-a-grid
2. 思路分析:
① 由题目的意思可以知道这是一个路径检测的连通性问题,所以首先可以想到的是可以使用dfs来解决,这个也是比较容易想到的,dfs可以尝试每一条可能的路径,假如存在一条符合的路径那么dfs可以找出来,需要注意的是因为使用dfs我们尽量不要写没有返回值的方法,因为没有返回的时候话会搜索网格中的所有路径,而我们的目标是只需要找到一条满足的路径即可,所以最好写有返回值的dfs这样找到一条可以到达网格中的最后节点的路径可以层层返回true
② 第一步是确定使用dfs来解决,第二步是确定dfs解决的各种细节,也就是大概的思路,在一开始做的时候发现这道题目的难点是一个方块可以看成两个入口和两个出口,所以需要判断出是到达当前方块的是哪一个入口与哪一个出口,这样在dfs递归调用的时候才知道下一次是哪一个入口和出口,这样才可以判断出路径是否有效
③ 基于这个特点可以想到使用map来进行映射,我们可以这样映射,键表示的是当前方块的数值,也就是题目中的123456表示的方块,值为map,map可以表示这个方块的两个入口与出口,这样的在递归的时候就可以很好判断出当前方块的出口是什么方向的了,入口和出口的方向可以使用1234来表示,分别对应上下左右四个方向
④ 我们在一开始的时候需要判断出网格中的第一个方块的数值是什么,这样才好判断出方块中所代表的路径对应的出口方向是什么,使用一个四次的for来表示上下左右四个方向,我们在尝试是否可以到达下一个网格的时候就可以map来获取对应的值,也就是获取下一个网格的出口位置,假如值不为空那么表示到达当前的方块的路径是合法的
⑤ 此外有一些特殊的路径需要判断过滤一下,因为有一些路径只是方向上符合但是位置上不符合的这样的路径也是不符合的,我在提交到领扣上才可以发现这些错误,当当前是横着走或者是竖着走的时候(网格数值为1或2)是不需要改变方向的,因为它是一直这样走,只有当网格数值为3,4,5,6才需要改变方向,获取出口
3. 代码如下:
我自己写的:
class Solution {
Map<Integer, Map<Integer, Integer>> map = new HashMap<Integer, Map<Integer, Integer>>(){
{
/*两个方向的映射*/
put(1, new HashMap<Integer, Integer>(){
//值中的map表示这个方块的入口和出口的方向, 1234对应上下左右
{put(3, 4);put(4, 3);}
});
put(2, new HashMap<Integer, Integer>(){
{put(1, 2);put(2, 1);}
});
put(3, new HashMap<Integer, Integer>(){
{put(4, 2);put(1, 3);}
});
put(4, new HashMap<Integer, Integer>(){
{put(1, 4);put(3, 2);}
});
put(5, new HashMap<Integer, Integer>(){
{put(2, 3);put(4, 1);}
});
put(6, new HashMap<Integer, Integer>(){
{put(2, 4);put(3, 1);}
});
}
};
public boolean hasValidPath(int[][] grid) {
if (grid == null) return false;
int curdir = -1;
if (grid[0][0] == 1 || grid[0][0] == 6) curdir = 4;
else if (grid[0][0] == 2 || grid[0][0] == 3 || grid[0][0] == 4) curdir = 2;
else curdir = 1;
vis = new int[grid.length][grid[0].length];
return dfs(curdir, grid, 0, 0);
}
/*表示向上下左右四个方向走*/
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
int vis[][];
private boolean dfs(int curdir, int[][] grid, int r, int c) {
/*递归的出口也是判断是否能够走出去的关键*/
if (r == grid.length - 1 && c == grid[0].length - 1)return true;
vis[r][c] = 1;
for (int i = 0; i < 4; ++i){
int x = r + dx[i];
int y = c + dy[i];
if (x >= 0 && x < grid.length && y >= 0 && y < grid[0].length && vis[x][y] == 0){
//获取网格出口方向
Integer cur = map.get(grid[x][y]).get(curdir);
if (cur != null){
/*这个与之前一样只要是有一个true那么就返回true因为这个时候我找到了一个合适的路径
* */
/*向右或者向下的方向不符合条件的需要去掉*/
if ((curdir == 1 || grid[x][y] == 1) && x > r) continue;
if ((curdir == 2 || grid[x][y] == 2) && r == x) continue;
/*方向相同的时候不需要改变方向: 这一点需要额外注意一下*/
/*当为横着走与竖着走的时候是不需要改变方向的这一点需要注意*/
if (grid[x][y] == 1 || grid[x][y] == 2) {
if (dfs(curdir, grid, x, y)) return true;
vis[x][y] = 0;
}else {
if (dfs(cur, grid, x, y)) return true;
vis[x][y] = 0;
}
}
}
}
return false;
}
}
在领扣中发现一位大佬写得代码不错,我使用java语言重新实现了一下,大佬题解:https://leetcode-cn.com/problems/check-if-there-is-a-valid-path-in-a-grid/solution/cdfsjie-fa-rong-yi-li-jie-dai-ma-duan-zhu-shi-duo-/
class Solution {
public boolean hasValidPath(int[][] grid) {
vis = new int[grid.length][grid[0].length];
m = grid.length;
n = grid[0].length;
int sta = grid[0][0]; //起点的拼图编号
for(int i = 0; i < 4; ++i)//朝着四个方向都试一下
if(pipe[sta][i] != -1)//当前方向可以走
if(dfs(0,0,pipe[sta][i],grid))//沿着当前方向搜索
return true; //拼图都有两个方向可以走,只要沿着一个初始方向走通就可以。
return false;
}
int m, n, dx[] = {1,0,-1,0}, dy[]={0,1,0,-1};//0下、1右、2上、3左
int pipe[][]={
{-1, -1, -1, -1},
{-1, 1, -1, 3},
{0, -1, 2, -1},
{-1, 0, 3, -1},
{-1, -1, 1, 0},
{3, 2, -1, -1},
{1, -1, -1, 2}
};
//记录各个拼图块路径的方向,0、1、2、3代表方向,-1代表不可走。
int vis[][];
private boolean dfs(int x, int y, int dir, int grid[][]) {
if(x == m - 1 && y == n - 1) return true;//到达终点
int xx = x + dx[dir];
int yy = y + dy[dir]; //得到下一个准备走的坐标
if(xx < 0 || yy < 0 || xx >= m || yy >= n) return false;//越界
int nxt = grid[xx][yy]; //得到下一块拼图的编号
if(pipe[nxt][dir] != -1 && vis[xx][yy] == 0)
return dfs(xx, yy, pipe[nxt][dir], grid); //如果当前方向可走,则方向改变,继续走。
return false;
}
}