一、题设
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入: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 个单位的雨水(蓝色部分表示雨水)。
示例 2:
输入:height = [4,2,0,3,2,5] 输出:9
二、基本思路与代码实现
1.按层暴力求解:
按第i行进行遍历,比较height[ i ]与i的大小:
1.若height [ i ] >= i : res += temp,temp = 0
2.若height [ i ] <i : temp += 1
很可惜,这样做可恶的超出了时间限制。
def trap(self, height):
# 解法1 : 按第i行进行遍历,看height[i]与i的大小比较
# 若height[i] >= i: res += temp temp = 0
# 若height[i] < i: temp += 1
res = 0
max_water_level = 0
for i in range(len(height)):
if height[i] > max_water_level:
max_water_level = height[i]
for i in range(1,max_water_level + 1):
temp = 0
isStart = False
for j in range(len(height)):
if height[j] < i and isStart:# 之前没有比i高的柱子就没有水
temp += 1
if height[j] >= i:
res += temp
temp = 0
# 之前有比i高的柱子了
isStart = True
return res
2.聚焦于求每列的水:
1. 首先求[0 , i-1]中最高的柱子。
2. 其次求[0 , i - 1]和[i + 1 , len - 1]中最高的柱子。
3. 比较当前列高度和左右最高柱子的较低值,若当前列高度大,则没水;若当前列高度小,则水量为 左右最高柱子的较低值 - 当前柱子高度。
这种做法产生了比较有意思的现象:java可以AC,而python差两个用例AC,在处理高并发、多数据量的情况下,java还是有得天独厚的优势!
python:
def trap(self, height):
# 解法2 : 聚焦于求每列的水
# 如何求某列的水:1.求[0,i-1]中最高的柱子 2.求[i+1,len-1]中最高的柱子 3.比较当前列高度和左右高柱子的较低值
def max_height(nums,start,end):
max_val = 0
for index in range(start,end+1):
if nums[index] > max_val:
max_val = nums[index]
return max_val
res = 0
lens = len(height)
for i in range(1,lens-1):
left_max = max_height(height,0,i-1)
right_max = max_height(height,i+1,lens - 1)
lower_max = min(left_max,right_max)
if lower_max > height[i] :
res += (lower_max - height[i])
return res
java:
public int trap(int[] height) {
int res = 0, i,j;
for(i = 1 ; i < height.length - 1 ; i++){
int left_max = 0;
int right_max = 0;
for(j = 0 ; j <= i-1 ; j++){
if(height[j] > left_max)
left_max = height[j];
}
for(j = i+1 ; j < height.length ; j++){
if(height[j] > right_max)
right_max = height[j];
}
int lower_max = Math.min(left_max,right_max);
if (lower_max > height[i]){
res += (lower_max - height[i]);
}
}
return res;
}
3.动态规划
另外定义数组:max_left[n]、max_right[n]
1.max_left[i] 代表前[0,i-1]中的最大值,比较max_left[i-1]与height[i-1]即可
2.max_right[i]代表后[i+1,n]中的最大值,比较max_right[i+1]与height[i+1]即可
3.最后在[1,lens-1]中判断height[i]与max_left[i]和max_right[i]的小值哪个大,若height[i]大,则height没有水;若height[i]小,则盛水量即为两值之差。
def trap(self, height):
res = 0
lens = len(height)
max_left,max_right = [0] * lens,[0] * lens
for i in range(1,lens):
max_left[i] = max(max_left[i-1],height[i-1])
for i in range(lens-2,-1,-1):
max_right[i] = max(max_right[i+1],height[i+1])
for i in range(lens-1):
lower_max = min(max_left[i],max_right[i])
if lower_max > height[i]:
res += (lower_max - height[i])
return res
4.栈(参考括号匹配的问题)
1.当栈为空或者栈顶元素>=当前下标元素时,height数组值在呈现下降的趋势,即当前位于坑的左边,所以这时还不知道这个水坑的深度有多少,所以继续往栈内存。
2.当栈不为空或者栈顶元素<当前下标元素时,height数组值在呈现上升的趋势,即当前位于坑的右边,此时水坑的深度已经知道了,所以可以开始计算水量,每上一个台阶的时候,计算当前的水量即可。
3.水量的计算:首先计算当前水量的长度distance,即当前下标值 - 栈顶存放的下标 - 1;其次计算高度height,即栈顶下标对应的值和当前下标对应值的较小值;最后计算水量的面积的时候要减掉上次计算过水量的高度值,即 s = distance * (height - h),其中 h 在每次计算前都等于前一个出栈的下标对应的值。
def trap(self, height):
# 解法5 : 栈
# helper[]
current_index,lens = 0,len(height)
helper = []
res = 0
while current_index < lens:
# 栈不为空且当前指向高度大于栈顶元素高度
while helper and height[helper[-1]] < height[current_index]:
x = height[helper[-1]]
helper.pop()
if not helper:
break
# 计算当前存储的水量
distance = current_index - helper[-1]- 1
hit = min(height[current_index],height[helper[-1]])
res += (distance * (hit -x ))
helper.append(current_index)
current_index += 1
return res