接雨水II

407. 接雨水 II - 力扣(LeetCode)

class Solution {
    static constexpr int DIRS[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};

public:
    int trapRainWater(vector<vector<int>>& heightMap) {
        int m = heightMap.size(), n = heightMap[0].size();

        priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<>> pq;
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (i == 0 || j == 0 || i == m - 1 || j == n - 1)
                {
                    pq.emplace(heightMap[i][j], i, j);
                    heightMap[i][j] = -1;
                }
            }
        }

        int ans = 0;
        while (!pq.empty())
        {
            auto [dig, i, j] = pq.top();
            pq.pop();
            for (auto& [dx, dy] : DIRS)
            {
                int nx = i + dx, ny = j + dy;

                if (0 <= nx && nx < m && 0 <= ny && ny < n && heightMap[nx][ny] >= 0)
                {
                    ans += max(dig - heightMap[nx][ny], 0);

                    pq.emplace(max(dig, heightMap[nx][ny]), nx, ny);
                    heightMap[nx][ny] = -1;
                }
            }
        }

        return ans;
    }
};

📝 算法笔记:3D 接雨水 (Trapping Rain Water II)

1. 算法分类与核心思想

  • 算法分类优先队列 (Priority Queue) + BFS,也常被称为“Dijkstra-like BFS”。
  • 核心原理:木桶效应 (Wood Bucket Theory)
    • 水能蓄多高,不取决于周围最高的板,而是取决于最低的那块木板
    • 为了找到这个“最低木板”,我们使用最小堆(Min-Heap)始终处理当前边界中最矮的格子。

2. 代码逻辑三步走

第一步:找边界(初始化)
  • 把矩阵最外圈的所有格子全部放入最小堆 pq
  • 最外圈的格子是装不住水的,它们是最初的“木桶壁”。
  • 将处理过的格子标记为 -1(防止重复访问)。
第二步:由外向内渗透(贪心搜索)
  • 每次从堆中弹出高度最低的格子 (dig, i, j)
  • 贪心逻辑:既然它是目前围墙里最矮的,那么它能承载的水位就决定了它邻居的蓄水上限。
第三步:填坑与更新围墙

遍历当前格子的邻居 (nx, ny)

  1. 算水量:如果邻居比 dig 矮,说明邻居是个坑。
    • 接水量 = max(0, dig - 邻居高度)
  2. 移围墙:邻居被处理后,就成了新的围墙。
    • 新高度更新max(dig, 邻居高度)
    • 解释:如果邻居矮,填水后高度变成 dig;如果邻居高,它本身就是更高的新围墙。
  3. 入堆:把新高度和坐标放入堆中,继续循环。

3. 关键代码片段解析

  • 最小堆定义:

    priority_queue<..., greater<>> pq; —— 必须是 greater,保证弹出的是最矮的板。

  • 蓄水公式:

    ans += max(dig - heightMap[nx][ny], 0); —— 只有内部比边界低才能接水。

  • 边界更新(核心):

    pq.emplace(max(dig, heightMap[nx][ny]), nx, ny); —— 无论接不接水,邻居都变成了新围墙的一部分,其高度是“注水后”的高度。


4. 复杂度与注意事项

  • 时间复杂度:$O(MN \log(MN))$。每个格子进出堆一次,堆操作为对数级别。
  • 空间复杂度:$O(MN)$。用于堆存储和原数组标记。
  • 易错点
    1. 行列对应m 对应 heightMap.size() (行),n 对应 heightMap[0].size() (列)。
    2. 标记位:记得给访问过的格子打标记(如 =-1),否则会陷入死循环。
    3. 长方形边界:判断边界时 i == m - 1j == n - 1 不要写混。

5. 总结口诀

最矮边界先出堆,看向邻居填水坑。

水满之后围墙移,高度取大再入堆。

posted @ 2025-12-22 09:32  belief73  阅读(0)  评论(0)    收藏  举报