前两天,听同事说什么接雨水问题。之前没看过,出于好奇,“力扣”上搜索了一下。读了一下题目,思索了一会,嗯,还算可以。“不传谣,不信谣。”不难,也不简单,比较适中。
题目:
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
分析:
1.能储水的地方都是“两端高,中间低”的特点。
2.每个坑能储水的高度,都是以两边最低的为准。
3.假设每个储水的坑,以最高的为一边,另一边需要
分别在这最高边的两侧找仅次于他的高度边。标记两个坑后,在以次边向后延伸,找相对于当前边的次边。标记坑后,以此类推。走到最后的边界为止。
4.当前边与次边形成的坑,必须不相邻,否则存不住水。
思路:
通过以上分析,上手搞一下。简单说一下代码流程。
1.首先找到最高的柱子Max,划分为两段分别找水坑。
2.分别向前后寻找,仅次于Max的的柱子。找到后并标记Max1,
(相邻的话不计为Max1,跳过计算,并把当前的Max1记为Max,继续寻找。)
记录Max与Max1中间的柱子。Max与Max1中间的水柱机位Hn,水坑储水总量为,
Max1 - H1到Max-Hn的和。
3.递归累计。
代码:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
public class TheRainTest {
public static void main(String[] args) {
int[] height = {5,8,9,4,9,6,1,4};
System.out.println(new TheRainTest().trap(height));
}
public int trap(int[] height) {
//判断是否可以储水
if(Objects.isNull(height) || height.length == 0 || height.length == 1 || height.length == 2){
return 0;
}
//regin max最高值,maxIndex最高值下标(寻找最高柱子)
int max = 0,maxIndex = 0;
for(int i = 0 ; i < height.length; i++){
if(max < height[i]){
max = height[i];
maxIndex = i;
}
}
if(max == 0){
return 0;
}
//endregion
List<int[]> befores = new ArrayList<>();
List<int[]> afters = new ArrayList<>();
//记录前半段每个水坑的柱子
beforeCollect(height,maxIndex,befores);
//记录后半段每个水坑的柱子
afterCollect(height,maxIndex,afters);
//总面积
int sum = sumBefore(befores) + sumAfter(afters);
return sum;
}
/**
* 计算每段坑的储水总和
* @param befores 最高柱子前半段的坑段
* @return 前半段储水和
*/
public int sumBefore(List<int[]> befores){
if(befores.size() == 0){
return 0;
}
int sum = 0;
for(int[] tempCollect : befores){
int max = tempCollect[0];
for(int i = 1; i < tempCollect.length; i++){
sum += max - tempCollect[i];
}
}
return sum;
}
/**
* 计算每段坑的储水总和
* @param afters 最高柱子后半段的坑段
* @return 后半段储水和
*/
public int sumAfter(List<int[]> afters){
if(afters.size() == 0){
return 0;
}
int sum = 0;
for(int[] tempCollect : afters){
int max = tempCollect[tempCollect.length-1];
for(int i = tempCollect.length-2; i >= 0; i--){
sum += max - tempCollect[i];
}
}
return sum;
}
/**
* 收集最高峰前,坑最大的区间段
* @param height 总段
* @param maxIndex 最大的柱子高
* @param befores 记录每段的柱子
*/
public void beforeCollect(int[] height,int maxIndex, List<int[]> befores){
if(maxIndex == 0){
return;
}
int tempMax = 0,tempMaxIndex = 0;
for(int i = 0; i < maxIndex; i++){
if(height[i] >= tempMax){
tempMaxIndex = i;
tempMax = height[i];
}
}
if(tempMax == 0){
return;
}
if(maxIndex - tempMaxIndex > 1) {
int[] segment = Arrays.copyOfRange(height, tempMaxIndex, maxIndex);
befores.add(segment);
}
beforeCollect(height,tempMaxIndex,befores);
}
/**
* 收集最高峰前,坑最大的区间段
* @param height 总段
* @param maxIndex 最大的柱子高
* @param afters 记录每段的柱子
*/
public void afterCollect(int[] height,int maxIndex, List<int[]> afters){
if(maxIndex == height.length - 1){
return;
}
int tempMax = 0,tempMaxIndex = 0;
for(int i = maxIndex+1; i < height.length; i++){
if(height[i] >= tempMax){
tempMaxIndex = i;
tempMax = height[i];
}
}
if(tempMax == 0){
return;
}
if(tempMaxIndex - maxIndex >1){
int[] segment = Arrays.copyOfRange(height,maxIndex+1,tempMaxIndex+1);
afters.add(segment);
}
afterCollect(height,tempMaxIndex,afters);
}
}
总结:
中间没有考虑到边界,修改了两次通过了“力扣”的校验。其中,还有可以根据时间和空间改进的地方。或者换一个思路解决。自我觉得,如果我再考虑的话,还是需要找到最高的柱子来解题。小伙伴可以多多提意见,相互学习,开阔思路。