大家一起来攒青豆!

64 阅读2分钟

当青训营遇上码上掘金
阅前提示:本文是用来记录“「青训营 X 码上掘金」主题创作活动入营版”中,关于主题四:“攒青豆”的思路以及代码实现~

主题要求

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

image.png

解题思路

从题目的要求中我们可以很容易发现,接到的青豆数量是受限制于比较矮的柱子的高度。所以我们可以采用“滑动窗口”的思想来完成这一道题目(假设总共接到青豆数为total):

  • 设置两个指针分别指向最左端和最右端的柱子(表示为left和right)
  • 因为接到青豆数受限于矮的那根柱子,所以我们让left和right中高度低的向对方移动(假设这里是left)
  • 有关left指针的移动,有两个解决思路:
    • 用另一个变量temp记录left指针原本所在柱子的高度,比较temp的高度和当前left所指向柱子的高度:
      1.temp >= height[left]:total+=(temp-height[left]),left继续向右移动
      2.temp < height[left]: 比较当前left和right中的高度,并继续进行上述操作,直到left和right指向同一个柱子
    • 用另一个指针temp记录left要移动的下一个柱子,比较temp指向柱子和left指向柱子高度:
      1.height[temp] <= height[left]:total+=(height[left]-height[temp]),temp继续向右移动
      2.height[temp] > height[left]: left指向柱子变成temp指向柱子(即left=temp),然后再比较当前left和right中的高度,并继续进行上述操作,直到left和right指向同一个柱子

代码呈现

以下代码为第二种实现方式:

package main

import "fmt"

func main() {
	n := 0
	temp := 0
	fmt.Println("请输入柱子个数:")
	fmt.Scanf("%d", &n)
	fmt.Println("请分别输入柱子高度:")
	height := make([]int, n)
	for i := 0; i < n; i++ {
		fmt.Scanf("%d", &temp)
		height[i] = temp
	}
	left := 0
	right := n - 1
	max := 0
	for left != right {
		if height[left] <= height[right] {
			temp = left
			for temp != right {
				temp = temp + 1
				if height[left] < height[temp] {
					break
				}
				max = max + height[left] - height[temp]
			}
			left = temp
		} else {
			temp = right
			for temp != left {
				temp = temp - 1
				if height[right] < height[temp] {
					break
				}
				max = max + height[right] - height[temp]
			}
			right = temp
		}
	}
	fmt.Printf("可以接%d个单位的青豆\n", max)
}

复杂度

因为采取的解题方法需要对所有柱子高度进行遍历,所以时间复杂度:O(n)
因为要保存所有柱子的高度,所以空间复杂度:O(n)