题目来源LeetCode第42题
根据别人大佬思想,自己慢慢领悟后总结,java编写出来的四个解该题的方法,我也是菜鸟一枚,写了很久的,所以做个笔记大家分享,大家指出不足,麻烦点个赞吧,谢谢
首先是用于测试的主函数
public static void main(String[] args) {
int[] height=new int[]{6,8,5,0,0,6,5};
System.out.println(new Solution().trap1(height));
System.out.println(new Solution().trap2(height));
System.out.println(new Solution().trap3(height));
System.out.println(new Solution().trap4(height));
}
method1
思路:
- 按层分割,在每一层遍历遍每堵墙
- 若当前墙小于该层则遇到积水沟,则addWater++;(初始条件不可刷新addWater)
- 若当前墙大于该层,则收集该积水sum+=addWater;
- 直至每层的每堵墙判断完
- 时间复杂度O(m*n)
- 空间复杂度O(1)
/*
获取层数
*/
public int getFloors(int[] height){
int min=0,max=0;
for (int i=0;i<height.length;i++){
if(height[i]>=max){
max=height[i];
}else if(height[i]<=min){
min=height[i];
}
}
return max-min;
}
/**
* 按层数求积水
* @param height
* @return
*/
public int trap1(int[] height) {
int sum=0;
int floors=getFloors(height);
//每一层搜索积水
for(int floor=1;floor<=floors;floor++){
int addWater=0;
//是否可以刷新addWater
boolean refreshAble=false;
for (int i=0;i<height.length;i++){
if(refreshAble&&height[i]<floor){
addWater++;
}else if(height[i]>=floor){
sum+=addWater;
//addWater用了之后归零
addWater=0;
refreshAble=true;
}
}
}
return sum;
}
method2
思路:
- 遍历每堵墙,并且找出每堵墙的左右边最高墙(left_max和right_max),找出后比较两堵墙,选择一个较矮的墙between_min,然后判断当前墙是否存在积水( if(between_min>height[i]) ),若存在则收集在当前墙处的积水sum+=(between_min-height[i]);
- 上述步骤直至每堵墙判断完
- 时间复杂度O(n2)
- 空间复杂度O(1)
/**
* 按列(高度)求积水
* @param height
* @return
*/
public int trap2(int[] height){
int sum=0;
//遍历每堵墙height[i],并且找出每堵墙上是否有积水
//两边的墙不用考虑,因为边界不存在积水
for (int i=1;i<height.length-1;i++){
int left_max=0,right_max=0;
//找出当前墙左边最高的墙
for (int left=0;left<i;left++){
if(height[left]>left_max){
left_max=height[left];
}
}
//找出当前墙左边最高的墙
for (int right=i+1;right<height.length;right++){
if(height[right]>right_max){
right_max=height[right];
}
}
//两堵最高墙之间较矮的墙 如果较矮墙高于当前墙 则计算较矮的墙与当前墙之间的差(积水深度)
int between_min=Math.min(left_max,right_max);
if(between_min>height[i]){
sum+=(between_min-height[i]);
}
}
return sum;
}
method3
思路:
- 我已在代码注释中说明了,如下
- 时间复杂度O(n)
- 空间复杂度O(n)
/**
* 同第二种解法,只不过寻找当前墙的左边最高墙与右边最高墙时,方法简化了,利用动态规划寻找,节约时间复杂度
* 寻找的思路:
* 如果(当前墙)的(左边墙)的(左边最高墙)比(左边墙)高,那么(当前墙)的(左边最高墙) = (左边墙)的(左边最高墙),反之为(当前墙)的(左边最高墙) = (左边墙)
* 寻找右边最高墙的思路与上面一样
* 把每堵当前墙的左右边最高墙,分别保存在两个数组里left_max[] right_max[]
* @param height
* @return
*/
public int trap3(int[] height){
int sum=0;
int[] left_max=new int[height.length];
int[] right_max=new int[height.length];
//最左边的墙不遍历
for (int left=1;left<height.length-1;left++){
left_max[left]=Math.max(left_max[left-1],height[left-1]);
}
//最右边的墙不遍历
for (int right=height.length-2;right>=0;right--){
right_max[right]=Math.max(right_max[right+1],height[right+1]);
}
//边界墙不存在积水,则排除
for (int cur=1;cur<height.length-1;cur++){
//求两堵最高墙之间较小的墙between_min
int between_min=Math.min(left_max[cur],right_max[cur]);
//如果between_min比当前墙高,则存在积水
if(between_min>height[cur]){
sum+=(between_min-height[cur]);
}
}
return sum;
}
method4
思路:
- 代码注释已说明,如下
- 时间复杂度O(n)
- 空间复杂度O(n)
/**
* 利用栈
* @param height
* @return
*/
public int trap4(int[] height){
int sum=0;
Stack<Integer> stack=new Stack<Integer>();
int cur=0;
while (cur<height.length){
//判断当前墙是否大于栈顶(存在积水沟),1、若ture,则弹出栈顶(加一个栈判空条件),计算新的栈顶与当前墙之间的积水
//若当前墙也大于新的栈顶重复1操作,2、反之跳出循环,把当前墙入栈
while (!stack.empty()&&height[cur]>height[stack.peek()]){
int peek=height[stack.pop()];
//弹出后再次判断栈是否为空,空则break
if(stack.empty()){
break;
}
//计算当前墙与新的栈顶之间的距离
int width=cur-stack.peek()-1;
//获取当前墙与新的栈顶之间较矮的墙
int min=Math.min(height[stack.peek()],height[cur]);
//计算积水
sum+=width*(min-peek);
}
stack.push(cur);
cur++;
}
return sum;
}