π Advanced Graph: Swim in Rising Water
π Description
LeetCode 778
You are given an n x n integer matrix grid where each value grid[i][j] represents the elevation at that point (i, j). The rain starts to fall. At time t, the depth of the water everywhere is t. You can swim from a square to another 4-directionally adjacent square if and only if the elevation of both squares individually are at most t. You can swim infinite distances in zero time. Return the least time until you can reach the bottom right square (n - 1, n - 1) if you start at the top left square (0, 0).
Real-World Application
This models Pathfinding with Bottleneck Constraints, such as finding a route for a vehicle that can only traverse terrain below a certain height, or network routing where links have varying bandwidths and you need the path with the best "minimum bandwidth".
π οΈ Constraints & Edge Cases
- \(n \le 50\)
- \(0 \le grid[i][j] < n^2\)
- Edge Cases to Watch:
n=1(Time is justgrid[0][0]).- Start or End points have high elevation.
π§ Approach & Intuition
The Aha! Moment
This isn't just "shortest path" (fewest steps). It's a minimax path problem β the goal is to minimize the maximum elevation along a path from top-left to bottom-right.
π’ Brute Force (Naive)
Binary search on the answer T. For a fixed T, run BFS/DFS to check if a path exists where all visited cells β€ T.
- Time Complexity: \(O(N^2 \log (\text{max_elevation}))\)
- Space Complexity: \(O(N^2)\)
- Works, but often slower and less elegant than Dijkstra or Union-Find.
π Optimal Approach 1: Dijkstra (Min-Heap)
- Maintain a min-heap storing
(time, r, c)βtimeis the water level needed to reach(r,c). - Start: push
(grid[0][0], 0, 0)and mark(0,0)visited. -
While heap is not empty:
-
Pop
(t, r, c). - If
(r, c) == (N-1, N-1), returnt. -
For each 4-directional neighbor
(nr, nc):new_t = max(t, grid[nr][nc])- If
(nr, nc)not visited, push(new_t, nr, nc)and mark visited.
Mermaid diagram for Dijkstra:
graph TD
Start["(0,0): Val 3"] -->|"Push (3,0,0)"| H1["Heap"]
H1 -->|"Pop min β (3,0,0)"| P1["Explore neighbors"]
P1 -->|"to (0,1): max(3,2)=3"| H2["Push (3,0,1)"]
P1 -->|"to (1,0): max(3,10)=10"| H3["Push (10,1,0)"]
H2 -->|"Pop (3,0,1)"| Next["Continue..."]
π Optimal Approach 2: Union-Find (Kruskal-style)
- Sort all cells by elevation.
- Initialize each cell as its own component.
-
Iterate over cells in increasing elevation:
-
For each cell, union it with adjacent cells that are already βaddedβ.
- After each union, check if
(0,0)is connected to(N-1,N-1). - First elevation when start and end are connected = minimum required time.
Mermaid diagram for Union-Find:
graph TD
A["All cells sorted by elevation"] --> B["Add (0,1): val=2"]
B --> C["Add (0,0): val=3"]
C -->|"Union (0,0) β (0,1)"| D["Connected Component"]
D --> E["Add (1,0): val=10"]
E -->|"Union with neighbors"| F["Expand Component"]
F --> G["Check: Start β End connected?"]
G -->|No| H["Continue adding cells"]
G -->|Yes| I["Return current elevation"]
π» Solution Implementation
β±οΈ Complexity Analysis
| Approach | Time | Space |
|---|---|---|
| Dijkstra | \(O(N^2 \log N)\) β heap operations for all \(N^2\) cells | \(O(N^2)\) β visited set + heap |
| Union-Find | \(O(N^2 \log N)\) β sorting + union-find operations | \(O(N^2)\) β parent/size arrays |
π€ Interview Toolkit
- Dijkstra: More intuitive; easy to implement using a min-heap. Good if you want step-by-step path exploration.
- Union-Find: Conceptually elegant; βflood-fillβ approach. Often faster in practice for dense grids.
- Both approaches are correct; highlighting both demonstrates deep understanding.
π Related Problems
- Network Delay Time β Standard Dijkstra