「青训营 X 码上掘金」主题 4—攒青豆

61 阅读2分钟

当青训营遇上码上掘金,第一次参加这个活动。本人选择的是主题4: 题目:

现有 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 个单位的青豆。
  • 这道题可以使用几种不同的方式解决,本文介绍如何使用双指针来解决本题,时间复杂度为O(N),额外空间复杂度为O(1)。
  • 思想主要为先计算每一列可以存储的青豆数,然后将总和加在一起得出问题答案。
  • 很明显,其中某列存储的青豆数量取决于这一列左侧的柱子的最大高度和右侧柱子的最大高度中的最小高度。
  • 双指针的解题思路就是从最左侧柱子和最右侧柱子开始遍历,如果左边的最大值更小就从左边开始遍历,如果右边的最大值更小就从右边开始遍历。
  • 最左侧与最右侧一定为柱子,也就是不可能存储青豆。

使用go语言实现:

package main

import "fmt"

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

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

func beans(height []int) int {
	nums := len(height)
	if nums <= 2 {
		return 0
	}
	leftMax := height[0]
	rightVal := height[nums-1]
	left := 1
	right := nums - 2
	ans := 0
	for left <= right {
		if leftMax < rightVal {
			ans += max(min(leftMax, rightVal)-height[left], 0)
			leftMax = max(leftMax, height[left])
			left++
		} else {
			ans += max(min(leftMax, rightVal)-height[right], 0)
			rightVal = max(rightVal, height[right])
			right--
		}
	}
	return ans
}

func main() {
	height := []int{5, 0, 2, 1, 4, 0, 1, 0, 3}
	ret := beans(height)
	fmt.Println(ret)
}