算法题 — 接雨水

给定 n 给非负整数,表示每个宽度为 1 的柱子的高度图,计算按照此排列的柱子,下雨之后能能接到多少雨水。

输入:height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]

输出:6

解释:上面是由数组 [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] 表示的高度图,在这种情况袭,可以接住 6 个单位雨水(蓝色部分表示雨水)。

就是用一个数组表示一个条形图,问这个条形图最多能接多少水。

1 暴力解法

具体来讲,对于位置 i 能装下多少水呢?

能装下 2 格。为什么呢?因为位置 i 处能达到的水柱的高度和其左边的最高柱子、右边的最高柱子有关。其左边最高柱子的高度是 2,其右边最高柱子的高度是 3,因为 height[i] 的高度是 0,所以位置 i 处能装下 2 格的水。

这里我们假设左右两个最高柱子的高度分别为 leftMax 和 rightMax,位置 i 处的水柱高度就是 min(leftMax, rightMax) - height[i]。

根据以上的思路:

kotlin 复制代码
fun trap(height: Array<Int>): Int {
    val n = height.size
    var res = 0

    // 位置 0 和位置 n - 1 处无法存储水
    for (i in 1 until n - 1) {
        var leftMax = 0 // 位置 i 处左边最高的柱子
        var rightMax = 0 // 位置 i 处右边最高的柱子

        // 寻找左边最高的柱子,这里到 i 是因为后面要 -height[i]
        for (j in 0..i) {
            leftMax = leftMax.coerceAtLeast(height[j])
        }
        // 寻找右边最高的柱子,这里从 i 开始是因为后面要 -height[i]
        for (j in i until n) {
            rightMax = rightMax.coerceAtLeast(height[j])
        }

        res += leftMax.coerceAtMost(rightMax) - height[i]
    }

    return res
}
2 备忘录优化

在暴力算法中,需要计算每个位置的 leftMax 和 rightMax,我们可以直接先把结果计算出来,不需要每次都遍历。

定义两个数组 leftMax 和 rightMax 充当备忘录,leftMax[i] 表示位置 i 左边最高的柱子高度,rightMax[i] 表示位置 i 右边最高的柱子高度。预先把这两个数组准备好,避免重复计算:

kotlin 复制代码
fun trap(height: Array<Int>): Int {
    val n = height.size
    var res = 0

    // 数组备忘录
    val leftMax = IntArray(n)
    val rightMax = IntArray(n)

    // 初始化
    leftMax[0] = height[0]
    rightMax[n - 1] = height[n - 1]

    for (i in 1 until n) {
        leftMax[i] = height[i].coerceAtLeast(leftMax[i - 1])
    }

    for (i in n - 2 downTo 0) {
        rightMax[i] = height[i].coerceAtLeast(rightMax[i + 1])
    }

    for (i in 1 until n - 1) {
        res += leftMax[i].coerceAtMost(rightMax[i]) - height[i]
    }

    return res
}

备忘录算法和暴力算法的思路差不多,就是避免了重复计算。

4 双指针解法
kotlin 复制代码
fun trap(height: Array<Int>): Int {
    val n = height.size
    var res = 0

    var left = 0
    var right = n - 1

    // 左边最高柱子的高度和右边最高柱子的高度
    var leftMax = height[0]
    var rightMax = height[n - 1]

    while (left < right) {
        leftMax = leftMax.coerceAtLeast(height[left])
        rightMax = rightMax.coerceAtLeast(height[right])

        if (leftMax < rightMax) { // 左边的柱子低于右边柱子的高度,水的高度只和较低的柱子有关
            res += leftMax - height[left]
            left++
        } else { // 右边的柱子低于左边柱子的高度,水的高度只和较低的柱子有关
            res += rightMax - height[right]
            right--
        }
    }

    return res
}
相关推荐
小O的算法实验室20 小时前
2026年IEEE TBD,面向大规模优化的随机矩阵粒子群算法,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
哭泣方源炼蛊20 小时前
AtCoder Beginner Contest 456 E补题(分层图 + 有向环检测 )
c++·算法·深度优先·图论·拓扑学
平行侠20 小时前
022Miller-Rabin 概率素性检验 - 概率与数论的完美联姻
数据结构·算法
ZC跨境爬虫20 小时前
跟着 MDN 学 HTML day_29:(动态构建与更新 DOM 树)
前端·javascript·ui·html·html5·媒体
编程技术手记20 小时前
html table布局平衡
前端·html
wuweijianlove20 小时前
算法与数据结构协同优化的设计思想的技术7
数据结构·算法
昵称小白20 小时前
二叉树专题(下)
算法·深度优先
故事和你9120 小时前
洛谷-数据结构2-1-二叉堆与树状数组1
开发语言·数据结构·c++·算法·动态规划·图论
huoyueyi20 小时前
3D数字孪生项目 LCP 优化指南
前端·3d·几何学
菜鸟小芯21 小时前
【腾讯位置服务开发者征文大赛】校园美食雷达 —— 基于 CodeBuddy + 腾讯 LBS 开发实战
前端·美食