leetcode
leetcode 2801 ~ 2850
迷路的机器人

迷路的机器人

难度:

标签:

题目描述

Imagine a robot sitting on the upper left corner of grid with r rows and c columns. The robot can only move in two directions, right and down, but certain cells are "off limits" such that the robot cannot step on them. Design an algorithm to find a path for the robot from the top left to the bottom right.

"off limits" and empty grid are represented by 1 and 0 respectively.

Return a valid path, consisting of row number and column number of grids in the path.

Example 1:

Input:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
Output: [[0,0],[0,1],[0,2],[1,2],[2,2]]

Note:

  • r, c <= 100

代码结果

运行时间: 23 ms, 内存: 16.6 MB


/*
题目思路:
使用深度优先搜索(DFS)来寻找从左上角到右下角的路径。首先,我们需要检查当前网格是否有效(即在边界内并且不是障碍物)。
然后我们尝试向下或向右移动。如果我们找到了一个有效的路径,我们返回这条路径。
如果当前路径不通,则回溯并尝试另一条路径。
使用Java Stream的递归特性和Optional类来处理路径查找和回溯。
*/
import java.util.*;
import java.util.stream.*;

public class RobotPathStream {
    public List<int[]> findPath(int[][] grid) {
        return findPath(grid, 0, 0).orElse(new ArrayList<>());
    }

    private Optional<List<int[]>> findPath(int[][] grid, int row, int col) {
        // 检查边界和障碍物
        if (row < 0 || col < 0 || row >= grid.length || col >= grid[0].length || grid[row][col] == 1) {
            return Optional.empty();
        }
        List<int[]> path = new ArrayList<>();
        path.add(new int[]{row, col});
        // 如果到达右下角,返回路径
        if (row == grid.length - 1 && col == grid[0].length - 1) {
            return Optional.of(path);
        }
        // 标记当前路径
        grid[row][col] = 1;
        // 尝试向下或向右移动
        Optional<List<int[]>> downPath = findPath(grid, row + 1, col);
        Optional<List<int[]>> rightPath = findPath(grid, row, col + 1);
        // 合并路径
        Optional<List<int[]>> resultPath = Stream.of(downPath, rightPath)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .findFirst();
        // 回溯
        grid[row][col] = 0;
        if (resultPath.isPresent()) {
            path.addAll(resultPath.get());
            return Optional.of(path);
        }
        return Optional.empty();
    }

    public static void main(String[] args) {
        RobotPathStream rps = new RobotPathStream();
        int[][] grid = {
            {0, 0, 0},
            {0, 1, 0},
            {0, 0, 0}
        };
        List<int[]> path = rps.findPath(grid);
        path.forEach(p -> System.out.println(Arrays.toString(p)));
    }
}

解释

方法:

这个题解首先判断右下角是否有障碍,如果有,则直接返回空路径。若没有障碍,从右下角开始,逆向考虑每个位置是否可以到达。使用标记值2代表可以向下走到达终点,标记值3代表可以向右走到达终点。这个标记过程是一个逆向的动态规划过程。开始于右下角,向左上角逆推,每个格子根据其右侧和下方的格子的标记来确定自己的标记。最后,如果左上角的格子被标记为可行(标记值大于1),则构造路径。路径的构造是根据每个格子的标记决定下一步是向下还是向右移动。

时间复杂度:

O(r*c)

空间复杂度:

O(r*c)

代码细节讲解

🦆
在题解中,为什么先检查右下角是否有障碍物,而不是从左上角开始检查?
在这个题解中,我们的目标是确认从左上角到右下角是否存在一条可行的路径。由于我们的路径构建是从右下角向左上角逆推的,如果右下角本身有障碍物,那么无论其他位置如何,路径都是不可达的。因此,检查右下角是为了立即决定是否终止算法,避免不必要的计算。
🦆
题解中提到使用标记值2和3来表示可以向下或向右行走,如果一个格子同时可以向下和向右行走怎样处理?
题解实际上没有考虑一个格子同时可以向下和向右行走的情形。标记值2和3仅表示首个可行的方向。在实现中,如果发现一个格子同时可以向下和向右行走,它将首先被标记为向下行走(标记值2)。只有当向下不可行时,它才可能被标记为向右行走(标记值3)。这种处理方式简化了逻辑,但可能不会探索所有可能的路径。
🦆
在反向标记过程中,对于边缘的格子(如最左列或最上行),题解如何确保不会访问到网格之外的元素?
题解中通过循环的起始和终止条件来保证不会访问网格之外的元素。对于行和列的遍历,都是从网格的最后一个索引开始,递减至0。在检查相邻的格子(向左或向上的格子)时,会首先检查索引是否大于0,确保不会访问到网格的边界之外。这种方式可以有效避免数组越界错误。

相关问题