主题 4:攒青豆思路详解(附Go和Java代码)

61 阅读2分钟

当青训营遇上码上掘金

题目

原题如下:

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

攒青豆.png

相信刷过LeetCode都知道这是很经典的一道"困难"题~,常见的解法便是--单调栈法,本文将重点对单调栈法进行讲解并解题.

栈的简介

栈是一种常用的数据结构,它最大的特点是“后入先出”,即后进入栈中的元素最先出来。为了确保“后入先出”的顺序,栈的插入和删除操作都发生在栈顶。 栈的操作可以用日常生活中的洗碗来理解。假设将洗好的碗堆成一摞。新洗的碗总是放在最上面,每次需要用碗的时候也总是从最上面拿。这一摞碗就相当于一个栈,放碗、取碗操作都发生在一摞碗的顶端,最后放入的碗最先被取走。 在解决很多题目时,我们经常遇到读入的数据暂时用不上的情形,通常数据会先保存到一个数据容器中以后再用。如果数据保存的顺序和使用顺序相反,那么最后保存的数据最先使用,这与栈的“后入先出”特性很契合,可以考虑将数据保存到栈中。很多时候保存在栈中的数据是排序的。根据题目的不同,栈中的数据既可能是递增排序的,也可能是递减排序的。因此,有人将这种用排序的栈解决问题的方法称为单调栈法。下面将开始介绍本题如何使用单调栈法进行解决.

解题

思路:单调递减栈(相同值也更新入栈),柱高作为右墙依次入栈,出现入栈元素(右墙)比栈顶大时,说明在右墙左侧形成了低洼处,低洼处出栈并结算该低洼处能接的豆子. 代码如下:

func trap(height []int) int {
	var left, right, leftMax, rightMax, res int
	right = len(height) - 1
	for left < right {
		if height[left] < height[right] {
			if height[left] >= leftMax {
				leftMax = height[left] // 设置左边最高柱子
			} else {
				res += leftMax - height[left] // //右边必定有柱子挡水,所以遇到所有值小于等于leftMax的,全部加入水池中
			}
			left++
		} else {
			if height[right] > rightMax {
				rightMax = height[right] // //设置右边最高柱子
			} else {
				res += rightMax - height[right] // //左边必定有柱子挡水,所以,遇到所有值小于等于rightMax的,全部加入水池
			}
			right--
		}
	}
	return res
}