← Daily Logs
LeetCode 778

Swim in Rising Water

HardJul 26, 2026graphunion-findgridelevation-sweep

`minCostConnectPoints` turned the DSU loop into a from-scratch edge generator; this turns it into a from-scratch *node* activator instead — grid cells sorted by elevation get unioned with whichever neighbours are already active, and since every value from `0` to `n^2 - 1` appears in the grid exactly once, the elevation that finally connects `(0,0)` to `(n-1,n-1)` is the answer, caught the instant it happens rather than computed afterward.

The problem

Implement `swimInWater(grid)` where `grid[i][j]` is a distinct elevation in `[0, n^2)`. At time `t` the water level is `t`, and a swim between two adjacent cells is only possible once both cells' elevations are `<= t`. Return the minimum `t` at which `(0,0)` can reach `(n-1,n-1)`.

`grid = [[0,1],[2,3]]` returns `3` — no path exists at `t = 2`, since `(1,1)`'s elevation `3` is still above water, so `t` has to rise to `3` before every cell on the only path (`0 -> 1 -> 3` or `0 -> 2 -> 3`) is submerged.

The approach

Same idea as `minCostConnectPoints`: sort a list built from the grid, then let union-find (the same `find`/path compression as every earlier DSU entry) decide connectivity as that sorted list gets walked. What differs is what gets sorted and unioned — `minCostConnectPoints` sorted *edges* between fixed nodes; this sorts *cells* themselves and only unions a cell with a neighbour once both are 'active', meaning their elevation is `<= ` the current threshold.

A cell only unions with its 4-directional neighbours once it's marked active. Processing cells in ascending elevation order guarantees that by the time `(r, c)` is reached, every already-active neighbour has an elevation `<= grid[r][c]` and can be unioned immediately; a neighbour with a higher elevation simply isn't active yet and gets skipped until its own turn comes up.

Because `grid` uses every value from `0` to `n^2 - 1` exactly once, processing cells in elevation order is the same as sweeping `t` upward one submerged cell at a time. So instead of binary-searching for `t` and re-checking connectivity at each guess, the loop checks `find(start) === find(end)` right after each activation and returns that cell's own elevation the moment it's true — necessarily the smallest `t` at which the corners connect.

The solution

js
class Solution {
    /**
     * @param {number[][]} grid
     * @return {number}
     */
    swimInWater(grid) {
        const n = grid.length;
        const parent = Array.from({ length: n * n }, (_, i) => i);

        const find = (x) => {
            while (parent[x] !== x) {
                parent[x] = parent[parent[x]];
                x = parent[x];
            }
            return x;
        };

        const cells = [];
        for (let row = 0; row < n; row++) {
            for (let col = 0; col < n; col++) {
                cells.push([grid[row][col], row, col]);
            }
        }
        cells.sort((a, b) => a[0] - b[0]);

        const active = Array.from({ length: n }, () => new Array(n).fill(false));
        const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]];

        for (const [elevation, row, col] of cells) {
            active[row][col] = true;
            const id = row * n + col;

            for (const [dr, dc] of directions) {
                const newRow = row + dr;
                const newCol = col + dc;
                if (newRow >= 0 && newRow < n && newCol >= 0 && newCol < n && active[newRow][newCol]) {
                    const neighbourId = newRow * n + newCol;
                    const rootA = find(id);
                    const rootB = find(neighbourId);
                    if (rootA !== rootB) parent[rootA] = rootB;
                }
            }

            if (find(0) === find(n * n - 1)) return elevation;
        }

        return n * n - 1;
    }
}

Time O(n^2 log n)Space O(n^2)

All entries