给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
有高度差才能产生积水,判断一段积水的指标左右两侧的最矮高度和在这其中每段与最小高度的差,如何确定两段,一个东西的右侧比自己矮就可以认为自己是一侧
上面是错误的,真是情况是确定好左侧之后,找到比左侧大于等于的
上面还有错误,确定好左侧后,按照之前的算法会漏记
换了个想法,要做修正,当一个高点到不到可以大于等于自己的时候,left+1,重新找
思路还是有问题,右边和左边其实没有直接关系,只要中间有比他矮的就要算上
不考虑复杂度的情况,我们对每个区块找到自己两侧最高的就可以求出自己容纳多少水,平方的复杂度,问题转化为怎么高效率找到一个坐标两侧最高的坐标,左侧最高值易得,镜像也可以求出右侧的最大值,o(n)的复杂度
public int trap(int[] height) {
if(height==null || height.length<=0) return 0;
int[] left=new int[height.length];
int[] right=new int[height.length];
int max=height[0];
for(int i=1;i<height.length-1;i++){
left[i]=max;
max=Math.max(max,height[i]);
}
max=height[height.length-1];
for(int i=height.length-1;i>=1;i--){
right[i]=max;
max=Math.max(max,height[i]);
}
int result=0;
for(int i=1;i<height.length-1;i++){
int min=Math.min(left[i],right[i]);
if(min>height[i]) result+=min-height[i];
}
return result;
}
有问题
public int trap(int[] height) {
int left=-1;
int right=-1;
int result=0;
for(int i=0;i<height.length;i++){
if(left==-1){
if(i<height.length-1 && height[i+1]<height[i]){
left=i;
}
}else{
if(height[i]>=height[left] || ){
right=i;
int min=Math.min(height[left],height[right]);
for(int j=left+1;j<right;j++) result=result+min-height[j];
left=right;
right=-1;
continue;
}
if(right==-1 && i==height.length-1){
i=left;
left=-1;
}
}
}
return result;
}
public int trap(int[] height) {
错误的
int left=-1;
int right=-1;
int result=0;
for(int i=0;i<height.length;i++){
if(left==-1){
if(i<height.length-1 && height[i+1]<height[i]){
left=i;
}
}else{
if(height[i]>=height[left]){
right=i;
int min=Math.min(left,right);
for(int j=left+1;j<right) result+=min-height[i];
left=right;
}
}
}
return result;
}
错误的
public int trap(int[] height) {
int left=-1;
int right=-1;
int result=0;
for(int i=0;i<height.length;i++){
if(left==-1){
if(i<height.length-1 && height[i+1]<height[i]){
left=i;
}
}else{
if(i==height.length-1 || height[i+1]<height[i]){
right=i;
int min=Math.min(left,right);
for(int j=left+1;j<right) result+=min-height[i];
left=right;
}
}
}
return result;0
}
看答案后,又理解了两种做法
第一种使用栈来辅助,我们开始的错误思路便在于储存的信息过少。
public int trap(int[] height) {
int ret=0;
Stack<Integer> stack=new Stack();
for(int i=0;i<height.length;i++){
if(stack.isEmpty() || height[stack.peek()]>height[i]) stack.add(i);
else{
while(!stack.isEmpty() && height[stack.peek()]<=height[i]){
int pre=stack.pop();
if(!stack.isEmpty()){
int left=stack.peek();
ret+=(Math.min(height[i],height[left])-height[pre])*(i-left-1);
}
}
stack.add(i);
}
}
return ret;
}
还有一种解法可以减少循环的次数和减少动态规划内存的应用,就是双指针。
在上面我们有一个条件没有用,就是对一个区块值的求解只有矮的那一侧有效。
public int trap(int[] height) {
int ret=0;
if(height==null || height.length<2) return 0;
int leftMax=height[0];
int rightMax=height[height.length-1];
int left=1;
int right=height.length-2;
while(left<=right){
if(leftMax<=rightMax){
if(height[left]<leftMax) ret+=leftMax-height[left];
else leftMax=height[left];
left++;
}else{
if(height[right]<rightMax) ret+=rightMax-height[right];
else rightMax=height[right];
right--;
}
}
return ret;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。