topic
Given n non-negative integers representing the height map of each column with a width of 1, calculate how much rain the column can receive after it rains.
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
输入:height = [4,2,0,3,2,5]
输出:9
ideas
brute force solution
Although the violent solution is simple, I haven't reacted to the violent thinking of this question for a while, and it took me a long time to figure out what's going on.
First of all, the general idea is to traverse each column, that is, traverse each element of the array, and then traverse each element as the center, traverse to the left and right sides to find the highest column higher than it, so that the top of the column can be calculated. How much water can I take.
It doesn't matter if you don't understand it. I didn't understand it at first. Next, I will understand it in detail with the code:
1.
First, the outermost for loop: for i in range(1, size-1)
Traverse the pillars, each pillar is used as the bottom of the barrel, and look for the tall pillars to the left and right sides as the left and right sides of the barrel
Since the two columns at the beginning and the end of the array are boundaries, it is impossible for them to form a bucket at the bottom of the bucket, so when traversing, it starts from the second column and ends at the penultimate column, so it is range(1, size -1) instead of range(size)
2.
Next, get the column as the bottom of the barrel each time you traverse, take it as the center, traverse to the left and right sides, and find the tallest column higher than it, as the left and right sides of the bottom of the barrel
for i in range(1, size-1):
max_left,max_right = 0,0
# 寻找i左边最高的柱子
for j in range(i+1):
max_left = max(height[j],max_left)
# 寻找i右边最高的柱子
for k in range(i,size):
max_right = max(height[k], max_right)
ans += min(max_left,max_right) - height[i]
Here are a few points that are not easy to understand:
Why is it that when looking for pillars to the left and right, it has to start from itself and find the beginning and the end, instead of just looking at the two pillars to the left and right of it; and why look for the highest, not just higher than it:
Because it is easy to fall into a misunderstanding of thinking here, that is, to think that a column can catch water, as long as the two adjacent columns on its left and right are higher than it, and it can be surrounded by a bucket.
In fact, this is not the case. For a column to receive water, there must be columns higher than it on its left and right sides, but these two columns are not necessarily adjacent to it; and, these two columns are not only higher than it, but also the highest Yes, look at the picture to understand:
For example, in the above picture, column a is the column currently used as the bottom of the barrel. The columns -a1 and a1 on the left and right sides of a are indeed higher than it, and they are adjacent to it, but looking at the left and right sides, -a2 and a2 are more High, they besieged a larger barrel. So, traverse left and right to the beginning and end, and find the tallest column as the sides of the barrel.When we calculate water, don't think about how to directly calculate how much water is in the bucket from -a2 to a2 besieged city, but to consider how much water is above a certain column. Think of it as a histogram, each The water above each column is also column by column, so don't be fooled by the word "water".
For example, to only count the water above column a, the lower side of the bucket is -a2, the height is 2, and the width of a is 1, so the water above column a is 2.
In the same way, the water above the -a1 column can also be calculated. At this time, it should be noted that the height of the -a1 column itself is 1, which occupies space, so its own height should be subtracted.
Similarly, the water above the a1 column can also be calculated
Finally, add up the water above the three pillars of -a1, a, and a1, and you will get the water in the barrels of -a2 and a2.
In the same way, traverse each column that can be used as the bottom of the bucket, calculate the water above the bottom of each bucket, and finally add up to the final answer.- When traversing the pillars at the bottom of the barrel, we do not need the two pillars at the beginning and the end, because they cannot be used as the bottom of the barrel.
When looking for the highest barrel side pillars on the left and right, you should include the two pillars at the beginning and the end, because they can be used as barrel sides. - When looking for the left and right sides of the barrel, the column at the bottom of the current barrel is also included in the traversal range, because if there is no column higher than it on the left and right sides, then it is both the bottom of the barrel and the side of the barrel. It can be imagined that it is a piece The wooden stake, obviously, cannot catch water on it. Therefore, in the end, when calculating the water intake, the lower height of the barrel side - the height of the bottom of the barrel itself is used. For it, it subtracts itself and equals 0, which is just logical.
Of course, you can also not count the column at the bottom of the bucket in the traversal range when traversing, but when there is no column higher than it on the left and right, it is more troublesome to write code separately to deal with it, so this way Writing is a small skill.
def trap(height) -> int:
size = len(height)
ans = 0
# 遍历每个可以作为桶底的柱子,开头、结尾两个柱子不能作为桶底,不在遍历范围内
for i in range(1,size-1):
max_left,max_right = 0,0
# 寻找i柱子左侧比自己高的最高柱子,没有的话i自己就是最高柱子
# 开头的柱子可以作为桶边,在遍历范围内
for j in range(i+1):
max_left = max(height[j],max_left)
# 寻找i柱子右侧比自己高的最高柱子,没有的话i自己就是最高柱子
# 结尾的柱子可以作为桶边,在遍历范围内
for k in range(i,size):
max_right = max(height[k], max_right)
# 较低的桶边柱子高度 - 桶底柱子高度,就是桶底柱子上方的储水量
# 每个桶底柱子上方储水量累加,就是最终答案
ans += min(max_left,max_right) - height[i]
return ans
Time complexity: O(n^2), each time an element is traversed, it is necessary to traverse the array once
Space Complexity: O(1)
dynamic programming
When you understand the idea of the brute force solution, dynamic programming is easy.
The principle of dynamic programming is exactly the same as the brute force solution, except that the array is traversed in advance, traversed from left to right, and the height of the highest column on the left side of each column is found; it is traversed from right to left to find the right side of each column. the height of the tallest column.
In this way, the maximum height of the barrel sides on the left and right sides of each column is found in advance as the bottom of the barrel. Then you can directly calculate how much rainwater can be received on each column as the bottom of the bucket.
Like the brute force solution, when looking for the highest barrel side pillar, the first and last two pillars are within the search range.
When calculating rainwater, the two pillars at the beginning and the end cannot catch the water, so they are not included in the calculation.
def trap(height) -> int:
n = len(height)
# 存储最大高度的数组
max_left = [0]*n
max_right = [0]*n
# 从左往右遍历,寻找每个桶底柱子左侧的最高柱子
for i in range(n):
# i=0左侧开头柱子就是它自己
# 其他柱子自己和之前的比较,高的就是最高柱子
max_left[i] = max(height[i],height[i] if i==0 else max_left[i-1])
# 从右往左遍历,寻找每个桶底柱子右侧的最高柱子
for i in range(n-1,-1,-1):
# i=n-1右侧结尾柱子就是它自己
# 其他柱子自己和之前的比较,高的就是最高柱子
max_right[i] = max(height[i],height[i] if i==n-1 else max_right[i+1])
# 计算每个桶底柱子,上方能接多少水,此时计算范围是range(1,n-1)不含开头、结尾的柱子
ans = sum(min(max_left[i],max_right[i])-height[i] for i in range(1,n-1))
return ans
Time complexity: O(n), only traversing the array 3 times
Space complexity: O(n), extra space is used to store the maximum height of the array
Monotonic stack
Traverse each column, if the current column is higher than the column at the top of the stack, it means that the column at the top of the stack may be used as the bottom of the bucket, forming a bucket that can receive water. Pop out the column at the bottom of the bucket at the top of the stack. At this time, the column at the top of the stack is the left side of the bucket, and calculate the current water storage capacity of the bucket.
If the current column is still higher than the column at the top of the stack at this time, it means that this column was on the left side of the bucket before, and now it may also be used as the bottom of the bucket to receive water, and the column at the top of the stack is also popped up. At this time, the column at the top of the stack is new. To the left of the bucket, the water storage capacity of the new bucket is calculated.
Repeat the above cycle until the current column is lower than the column at the top of the stack. At this time, the column at the top of the stack cannot be the bottom of the bucket, and no more buckets can be formed, or the stack is empty, then continue to the next round of traversal.
Summarize:
The column currently traversed, seen as the right side of the bucket
The column at the top of the stack in the stack is seen as the bottom of the bucket
The column before the top of the stack in the stack, seen as the left side of the bucket
It is through looping and stacking operations to see if each column acts as the right side of the bucket, whether it can form a bucket with the previous column
When a bucket is formed, the water storage capacity at the bottom of the bucket is calculated. After the calculation is completed, the column at the bottom of the bucket is popped out of the stack.
Looking further ahead, can it form a barrel with the previous pillar?
and so on
def trap(height) -> int:
ans = 0
stack = []
for i in range(len(height)):
# 栈里有柱子,右柱 高于 桶底
while stack and height[i] > height[stack[-1]]:
# 此时栈顶 是桶底柱子的下标
bottom = stack.pop()
# 弹出桶底空栈了,说明这个桶没有左柱,跳出
if not stack:
break
# 否则,就是一个桶,计算这个桶底的储水量
# 桶底被弹出后,此时栈顶是左柱的下标
# 桶的宽度=右柱下标 - 左柱下标 -1
w = i - stack[-1] -1
# 桶能储水的高度 = 较低的桶边 - 桶底的高度
# 假如遇到 左柱、桶底 一样高的情况,则经过计算这个桶能储水的高度为0,无法储水
# 会继续while循环,继续向前寻找桶底、左柱
h = min(height[stack[-1]], height[i]) - height[bottom]
ans += w*h
# 当前右柱,计算完它作为右柱,和前面的柱子可能形成的桶的储水量后,它也入栈,作为后面柱子的左柱、桶底
stack.append(i)
return ans
Time complexity: O(n) for a single traversal, each bar is accessed at most twice (due to stack popping and popping), and both popping and popping the stack are O(1).
Space complexity: O(n). A stack takes at most O(n) space in a stepped or flat bar structure
double pointer
The usual for loop can be regarded as a single pointer, traversing from the beginning to the end
Double pointers can be imagined as two pointers, one traverses backward from the beginning, and one traverses forward from the end, until the two pointers meet, and the entire array is traversed.
We define two pointers left, right, left moves to the right from the beginning to traverse, and right moves to the left from the end to traverse
The element pointed to by the pointer, as the bottom of the bucket. But now there are two pointers left and right, which one to choose as the bottom of the barrel?
Choose the one with the lower height as the bottom of the bucket, because for a bucket to be able to receive water, the side of the bucket cannot be lower than the bottom of the bucket.
For example, if the current left points to a column with a height of 1, and the right points to a column with a height of 3, then the column pointed to by the left pointer is the bottom of the barrel.
Next, calculate how much water can be received above the column pointed to by the left pointer. How much water a bucket can receive is determined by the shorter side of the bucket. Add it to the column on the left side of the left pointer, and the maximum height is 2. Then the column with a height of 2 determines the current bucket bottom pointed to by left can be stored. 2-1=1 water.
Here you may ask, what if there is a column with a maximum height of 3, 30, or 300 in the column on the left side? Or use the maximum height of the column on the left - the height of the bottom of the barrel, wouldn't that be wrong?
In fact, our logic is that when choosing which of the columns pointed to by left and right is used as the bottom of the barrel, we always choose the shorter one as the bottom of the barrel.
Because if the above situation occurs, assuming that there is a column with a height of 30 on the left, then the left pointer will always stop at the 30 column, because no column traversed by the right pointer is higher than 30, and the bottom of the barrel is pointed to by the right. Here, unless the right encounters a column higher than 30 as the traversal progresses, the bottom of the barrel reaches the left side, and the left starts to move.
Therefore, it is impossible to choose the shorter left as the bottom of the barrel, and the maximum height of the left side will be higher than the height of the right.
The calculation of right is the same as that of left.
def trap(height) -> int:
ans = 0
left,right = 0, len(height)-1
left_max,right_max = 0,0
# 两个指针同时遍历数组,left从左向右遍历,right从右向左遍历
# 当两个指针相遇时,即left=right时,数组就遍历完了
while left < right:
# 用来保存指针扫描过的柱子的最大高度
left_max = max(left_max, height[left])
right_max = max(right_max, height[right])
if height[left] < height[right]:
ans += left_max - height[left]
left +=1
else:
ans += right_max -height[right]
right -=1
return ans
Time complexity: O(n)
Space Complexity: O(1)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。