LeetCode 42 用Python3来接雨水

LeetCode42

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

Example:

Input: [0,1,0,2,1,0,1,3,2,1,2,1]

Output: 6

解法一

先说说自己超时的算法思路,也顺便把一种暴力解法展示出来,然后介绍别人家的高级算法,也算是抛砖引玉。

很明显,刚看到这题,我就想着以为思路来考虑,什么意思呢?举个例子,如输入为[0,7,1,4,6],result为我们要返回的结果。首先我们需要知道有0的地方代表着一个可以存水的坑,然后进行如下操作:

  1. 先将两边的0去掉 => [7,1,4,6]
  2. result += 0的个数 * 非零元素中的最小值
  3. 原数组中每个数减去上一步中的最小值(小于0则保持为0)
  4. 返回第一步,直到数组个数小于3则退出

于是就有:

[7,1,4,6] => result += 0 => [6,0,3,5] => result += 1x3 => [3,0,0,2] => result += 2x2 => [1,0,0,0] => [1] => exit.

总的而言还是比较麻烦。代码逻辑没问题,就是时间复杂度较高,约为O(n^2)级别, 取决于其中数的分布。

与之相对应的就是一种的思维方式。

解法一

这里的解法与后面的相关解法出发思路都是相同的,即根据两端方块的高度来决定每一个方块的可储水量,然后进行累加,储水量求的是面积,而我们每次仅考虑一个方块,宽度为1,则数值上其就应该等于储水高度。

暴力法的思路就是遍历每一个方块,分别找到包含它本身向左、向右的最大高度left_max, right_max,那么很显然储水高度由left_max, right_max中的较小者以及自身的高度baseHeight来决定:

  • min(left_max, right_max) - baseHeight
def trap(height):
	n = len(height)
	left_max, right_max = 0, 0
	# 返回结果
	result = 0
	# 考虑每一个方块
	for i in range(n):
		# 它所有左边的方块中找到left_max
		for j in range(i, -1, -1):
			left_max = max(left_max, height[j])
		# 它所有右边的方块中找到right_max
		for j in range(i, n, 1):
			right_max = max(right_max, height[j])
		result += min(left_max, right_max) - height[i]
	return result

很明显,时间复杂度依然较高,为O(n^2)。考虑一种用空间换时间的方法。上一种暴力法我们每选择一个方块,就重新进行O(n)的遍历找到它的left_max, right_max,其实是可以通过先循环遍历记录下来,之后直接索引取值即可,也可以算的上是动态规划的一种思想。

def trap(height):
	n = len(height)
	left_max, right_max = 0, 0
	# 记录每个索引处的最大左、右高度
	left = [0] * n
	right = [0] * n
	# 返回结果
	result = 0
	# 从左边遍历 找到left_max
	for i in range(n):
		left_max = max(left_max, height[i])
		left[i] = left_max
	# 从右边遍历 找到right_max
	for j in range(n-1, -1, -1):
		right_max = max(right_max, height[j])
		right[j] = right_max
	# 然后就是熟悉的操作
	for k in range(n):
		result += min(left[k], right[k]) - height[k]
	return result

这就是所谓的动态编程。时间复杂度上优化到了O(n)级别,相应的开辟了两个数组保存最大高度,空间复杂度也为O(n)。

解法二

好了,在解法一的启发下,就可以献出大招,双指针法了。双指针法能更灵活地在移动指针的过程中确定,也就是我们的left_max, right_max,并且使用了常数级别的空间复杂度。废话不多说,看代码:

def trap(height):
	n = len(height)
	# 双指针
	i, j = 0, n-1
	result = 0
	# 如果left_max处的高度小于right_max处的高度
	# 我们有理由改变左指针i来累加储水量
	left_max, right_max = 0, 0
	while i < j:
		left_max = max(left_max, height[i])
		right_max = max(right_max, height[j])
		if left_max < right_max:
			result += left_max - height[i]
			i += 1
		else:
			result += right_max - height[j]
			j -= 1
	return result

其实仔细一看,思路和暴力法没差。但是相对而言,算法进步了不少。

解法三

这是一种借助的思路,来找到上面所提到的方法。其出发点就是,将所有遍历的索引入栈,当遍历到的方块高度大于栈顶的高度,这就意味着此刻栈顶的高度不仅小于当前遍历值,也小于它自身栈的前一个高度。也就是说我们就找到了界和baseHeight。看代码吧:

def trap(height):
	n = len(height)
	stack = []
	result = 0
	for i in range(n):
		# 栈非空时,不断比较弹出并累加result
		while stack and height[stack[-1]] < height[i]:
			# 弹出
			pop_ = stack[-1]
			stack.pop()
			#如果栈为空
			if not stack:
				break
			# 宽度
			w = i - stack[-1] - 1
			# 高度差
			# 界的最小值-基准高度
			h = min(height[i], height[stack[-1]]) - height[pop_]
			result += h
		# 入栈
		stack.append(i)
	return result
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Key Board

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值