算法题 — 接雨水

给定 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,因为 heighti 的高度是 0,所以位置 i 处能装下 2 格的水。

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

根据以上的思路:

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 充当备忘录,leftMaxi 表示位置 i 左边最高的柱子高度,rightMaxi 表示位置 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
}
相关推荐
__zRainy__2 分钟前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.40 分钟前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~2 小时前
CSS元素显示模式(display)
前端·css
luj_17682 小时前
星火科技助力边远地区防病攻坚
c语言·开发语言·c++·经验分享·算法
AI大模型-小华2 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
geats人山人海4 小时前
数据结构第六章c语言 树的存储下二叉树的概念和存储
c语言·数据结构·算法
做前端的娜娜子5 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅5 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
漂流瓶jz5 小时前
UVA-12627 奇怪的气球膨胀 题解答案代码 算法竞赛入门经典第二版
算法·图论·递归·aoapc·算法竞赛入门经典·uva·12627