🌊《溺水边缘的最短路径》——一场“游泳比赛”里的 Dijkstra 冒险

85 阅读2分钟

Imagine you are in a flooded grid. The water rises every minute. You start at the top-left corner and must reach the bottom-right. Each cell contains the time when it becomes swimmable. You can only swim into a cell if the water has risen to at least that cell's value. What is the minimum time you need to reach the destination?

用白话说就是:

每个格子是个水坑,数字表示水涨到那个高度才能游过去。你能上下左右游,目标是从左上角游到右下角,用最小时间达成目标。

表面看是 BFS,实则是 Dijkstra 的最小路径变种。我们不是在乎总花费最小,而是在乎 路径上遇到的最大数字最小

🎯关键词:

  • 最小堆(PriorityQueue)
  • cost 表示当前路径中遇到的最大格子值
  • Dijkstra思想:总是从当前可达的最小代价节点扩展
  • visited数组防止重复访问

class Solution { private static final int[][] DIRS = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

public int swimInWater(int[][] grid) {
    if (grid == null || grid.length == 0 || grid[0].length == 0) {
        return 0;
    }

    PriorityQueue<Element> minHeap = new PriorityQueue<>();
    minHeap.offer(new Element(0, 0, grid[0][0]));
    boolean[][] visited = new boolean[grid.length][grid[0].length];
    visited[0][0] = true;

    while (!minHeap.isEmpty()) {
        Element cur = minHeap.poll();
        for (int[] dir : DIRS) {
            int neiX = cur.x + dir[0];
            int neiY = cur.y + dir[1];

            if (isValid(grid, neiX, neiY) && !visited[neiX][neiY]) {
                int neiCost = Math.max(cur.cost, grid[neiX][neiY]);

                if (neiX == grid.length - 1 && neiY == grid[0].length - 1) {
                    return neiCost;
                }

                minHeap.offer(new Element(neiX, neiY, neiCost));
                visited[neiX][neiY] = true;
            }
        }
    }

    return 0;
}

private boolean isValid(int[][] grid, int i, int j) {
    return i >= 0 && i < grid.length && j >= 0 && j < grid[0].length;
}

static class Element implements Comparable<Element> {
    int x;
    int y;
    int cost;

    Element(int x, int y, int cost) {
        this.x = x;
        this.y = y;
        this.cost = cost;
    }

    @Override
    public int compareTo(Element other) {
        return Integer.compare(this.cost, other.cost);
    }
}

封装了 (x, y, cost),可读性大大提升,compareTo 优雅定义优先级。用 Java 写这题时,封装类是强烈推荐的写法,避免了混乱的多变量堆操作。

🗺️图解:解法演示(伪图)

每个格子代表水位:
[0, 2, 3]
[1, 4, 6]
[5, 8, 7]

起点 grid[0][0] = 0  
目标 grid[2][2] = 7

最优路径是:
(0,0) → (1,0)=1 → (2,0)=5 → (2,1)=8 → (2,2)=7
路径最大值是 8

但还有更优解!
(0,0) → (0,1)=2 → (1,1)=4 → (1,2)=6 → (2,2)=7
路径最大值是 7 ✅

所以答案是 7

🧪时间复杂度分析

  • 总共访问所有节点一次:O(n²)
  • 每次堆操作:O(log n²)
  • 总时间复杂度:O(n² log n)

空间复杂度同样是 O(n²),主要用于 visited 数组和优先队列。

我们追求的不是最短距离,而是路径上最高的障碍最小!Dijkstra 在这里被用来处理“最大值路径”的问题,而不是最小和,这种转换非常经典且实用。