当青训营遇上码上掘金 | 主题 4:攒青豆

44 阅读2分钟

题目要求

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

image.png 以下为上图例子的解析:

输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。

思路分析

首先要成功装下一单位的青豆,我们需要左右两方同时有一格高的柱子存在(不论间隔)。故我们需要从两个方向寻找最高的柱子,计算出这之间满足左右两方同时存在柱子所构成的高度,并且需要减去原本存在的柱子高度,才能得到正确的可容纳的青豆单元数。

以这样的思路,我们需要:

  1. 两个循环:从左右两方扫描并记录最大高度。
  2. 第三个循环:切去单侧的独柱,并减去原本存在的柱子高度,得到正确的结果。

既然参加了这次的青训营,我们就用Go实现
额外的,由于Go没有类似于变长数组的概念以及max() min()这样的方法,我们需要用到Slice来代替变长数组,并自己定义max() min()

func trap(height []int) (FinalBeans int) {
	n := len(height)
	leftMax := make([]int, n)
	leftMax[0] = height[0]
	for i := 1; i < n; i++ {
		leftMax[i] = max(leftMax[i-1], height[i])
	}//左方向扫描

	rightMax := make([]int, n)
	rightMax[n-1] = height[n-1]
	for i := n - 2; i >= 0; i-- {
		rightMax[i] = max(rightMax[i+1], height[i])
	}//右方向扫描

	for i, h := range height {
		FinalBeans += min(leftMax[i], rightMax[i]) - h
	}
	return
}//切除单侧高段,并减去当前格高

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

当然,此种方法并不是最好的算法,但是思路直观,更容易想到。此题和接雨水极为相似,可自行搜索,这里不再赘述。