leetcode
leetcode 901 ~ 950
不同路径 III

不同路径 III

难度:

标签:

题目描述

代码结果

运行时间: 28 ms, 内存: 16.1 MB


/*
 * 思路:
 * 使用Java Stream编写递归搜索方法来实现相同的逻辑。
 * 1. 找到起始位置和空格数量。
 * 2. 使用递归和流来处理搜索和回溯。
 */

import java.util.stream.Stream;

public class UniquePathsIIIStream {
    public int uniquePathsIII(int[][] grid) {
        int empty = 0, sx = -1, sy = -1;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[i].length; j++) {
                if (grid[i][j] == 0) {
                    empty++;
                } else if (grid[i][j] == 1) {
                    sx = i;
                    sy = j;
                }
            }
        }
        return dfs(grid, sx, sy, empty);
    }

    private int dfs(int[][] grid, int x, int y, int empty) {
        if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] == -1) {
            return 0;
        }
        if (grid[x][y] == 2) {
            return empty == -1 ? 1 : 0;
        }
        grid[x][y] = -1;
        empty--;
        int[] directions = {-1, 0, 1, 0, -1};
        int paths = Stream.of(1, 2, 3, 4)
                .mapToInt(i -> dfs(grid, x + directions[i], y + directions[i - 1], empty))
                .sum();
        grid[x][y] = 0;
        empty++;
        return paths;
    }
}

解释

方法:

题解采用深度优先搜索(DFS)策略,通过递归来探索所有可能的路径。首先,算法寻找起始点(值为1的格子),并计算网格中空白格子(值为0)的数量。之后,从起始点出发,向四个方向探索,每走过一个空白格子,就将其标记为不可再走(设为-1)。如果到达终点(值为2的格子),检查是否所有空白格子都已经走过,即剩余格子数left是否为0。如果是,说明找到了一条有效路径。每次探索完成后,将当前格子重置回可走状态,以便其他路径探索时使用。最终,返回从起始点到终点的所有有效路径的数量。

时间复杂度:

O(4^k)

空间复杂度:

O(k)

代码细节讲解

🦆
为什么在到达终点时要检查剩余格子数left是否为0来判断路径的有效性?
在这个问题中,我们的目标是找到一条从起点到终点的路径,该路径必须经过所有的空白格子(值为0的格子)。因此,当我们到达终点(值为2的格子)时,我们需要检查是否所有的空白格子都已经被走过。剩余格子数left是在递归过程中跟踪未被访问的空白格子的数量。如果left为0,这意味着所有空白格子都已经被访问过,因此这条路径是有效的。如果left不为0,说明还有空白格子未被访问,因此这条路径是无效的。
🦆
在DFS中,你是如何处理网格边界以及障碍(`grid[x][y] < 0`)的情况的?
在深度优先搜索(DFS)中,为了确保不越界并且不走进障碍格子,我们在递归函数的开始部分进行检查:如果坐标(x, y)超出网格的边界,或者grid[x][y]的值小于0(意味着该格子是障碍或已经被访问过),则递归函数立即返回0,表示从这个格子出发无法形成有效的路径。这种检查确保了DFS只在合法和可行的路径上进行。
🦆
为什么在每次递归完成后需要将当前格子重置回可走状态(即设置为0)?
在DFS过程中,我们暂时将走过的格子标记为-1,以避免在当前路径搜索中重复走过相同的格子。一旦完成从某个格子出发的所有可能的路径探索,我们需要将这个格子重置为0(可走状态),以便在探索其他可能的路径时该格子可以再次被使用。这种做法是回溯算法的一部分,它允许我们探索所有可能的路径组合,确保每种可能都被考虑到。
🦆
题解中提到每个格子至多被访问一次,这是否意味着算法能够避免不必要的重复计算?
是的,题解中的方法确实通过确保每个格子在每条路径中最多被访问一次来避免不必要的重复计算。通过将走过的格子标记为-1,我们阻止了在同一路径搜索中对同一个格子的重复访问,这提高了效率。此外,这种策略也意味着每条可能的路径都是独立考虑的,没有重复计算同一路径。这种方法有效地降低了计算复杂度,尤其是在大规模网格中。

相关问题

解数独

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用 '.' 表示。

 

示例 1:

输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:


 

提示:

  • board.length == 9
  • board[i].length == 9
  • board[i][j] 是一位数字或者 '.'
  • 题目数据 保证 输入数独仅有一个解

不同路径 II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 10 来表示。

 

示例 1:

输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

示例 2:

输入:obstacleGrid = [[0,1],[0,0]]
输出:1

 

提示:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j]01

单词搜索 II

给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words, 返回所有二维网格上的单词 。

单词必须按照字母顺序,通过 相邻的单元格 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。

 

示例 1:

输入:board = [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], words = ["oath","pea","eat","rain"]
输出:["eat","oath"]

示例 2:

输入:board = [["a","b"],["c","d"]], words = ["abcb"]
输出:[]

 

提示:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 12
  • board[i][j] 是一个小写英文字母
  • 1 <= words.length <= 3 * 104
  • 1 <= words[i].length <= 10
  • words[i] 由小写英文字母组成
  • words 中的所有字符串互不相同