每日一道leetcode(2026.04.27):检查网格中是否存在有效路径

0 阅读4分钟

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

2. 分析

这道题主要考察的是思路,而非性能。每种街道类型其实对应两种走法,比如Street1,虽然是从左到右的一条直道,但也可以是从右到左的走法,你很能想到测试用例里面一定有这种弯弯绕绕的路线等着咱们。

另一点需要注意的是,虽然起点是(0,0),但并不需要起点的入口是上方或者是左方,可以是任一一方,那么,从起点开始,就需要判断这个单元格的两个街道方向,是否有可能走到目的终点。下图的测试用例即为实际需要返回为true的案例。

在这里插入图片描述

基于这些分析,我的思路是把所有街道类型的走法给穷举出来,然后从起点开始,按照这个街道的两个通道方向往下找,主要计算下一个单元格的位置,判断是否超出界限,是否连通即可。

3. 代码实现

class Solution {
    // 入口和出口的朝向 0-up 1-right 2-down 3-left
    // 入口是当前单元格的入口,出口则是下一个单元格的入口
    private static final int[][][] path = new int[][][]{
            {{1, 1}, {3, 3}},
            {{0, 0}, {2, 2}},
            {{1, 0}, {2, 3}},
            {{2, 1}, {3, 0}},
            {{1, 2}, {0, 3}},
            {{0, 1}, {3, 2}}
    };

    private static final int[][][] next = new int[][][]{
            {{0, 1}, {0, -1}},
            {{1, 0}, {-1, 0}},
            {{1, 0}, {0, -1}},
            {{0, 1}, {1, 0}},
            {{-1, 0}, {0, -1}},
            {{0, 1}, {-1, 0}}
    };

    public boolean hasValidPath(int[][] grid) {
        if (grid.length == 1 && grid[0].length == 1) {
            return true;
        }
        int index = grid[0][0] - 1;
        // 首个位置需特殊处理
        int[][] firstPath = path[index];
        // 方向1
        int[][] nextCord = next[index];
        if (find(grid, nextCord[0][0], nextCord[0][1], firstPath[0][1])) {
            return true;
        }
        // 方向2
        return find(grid, nextCord[1][0], nextCord[1][1], firstPath[1][1]);
    }

    private static boolean find(int[][] grid, int x, int y, int lastDirection) {
        while (true) {
            // 是否走出边界外
            if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length) {
                return false;
            }
            int index = grid[x][y] - 1;
            boolean flag;
            int[][] ints = path[index];
            if (x == 0 && y == 0) {
                // 又回到了起点
                return false;
            }
            // 判断连通性
            if (ints[0][0] == lastDirection) {
                flag = true;
            } else if (ints[1][0] == lastDirection) {
                flag = false;
            } else {
                // 道路不通
                return false;
            }
            if (x == grid.length - 1 && y == grid[0].length - 1) {
                // 走到终点
                return true;
            }
            // 获取下一个位置
            int[] cord = next[index][flag ? 0 : 1];
            x += cord[0];
            y += cord[1];
            // 当前位置的出口方向
            lastDirection = flag ? ints[0][1] : ints[1][1];
        }
    }
}

在这里插入图片描述

4. 总结

补充说明一下path,通过三维数组定义每种街道的两种通道类型。 在这里插入图片描述 以这个Street3为例,如果是左上角进入,那么这个单元格的入口类型为1,对应的出口类型为2,但是,在判断连通性的时候,是用当前的出口与下个单元格的入口类型进行匹配,所以,此处我直接将这个出口类型2转换为了入口类型0,所以(1,0)是这么来的。换个放下,若成下方进,则入口类型为2,出口类型为1,再将出口类型转换为下个单元格的入口类型3,所以有了(2,3)。完整地,Street3的path数据即为{{1, 0}, {2, 3}}