2019.5.12 #程序员笔试必备# LeetCode 从零单刷个人笔记整理(持续更新)
这道题属于智力题的范畴,和之前一道盛水题极相似:LeetCode(11):盛最多水的容器 Container With Most Water(Java)。得益于这道题的思路,这道题很容易可以手撕出来。
主要思路有几种:
首尾逼近
从两端朝内逼近,每次更新较矮的端点minheight,在每次循环逼近过程中将小于minheight的差值即为可以接入的雨水。
这种思路可以想象成是从低层至高层判断盛水量的。同一种首尾逼近思路还有另一种算法,可以每次逼近较矮的一边而不是两端一起逼近。
暴力法
对于数组中的每一个元素,其容纳雨水最大水位等于两边杆的最大高度的最小值减去它自身的高度。暴力法只是提供一个可供优化的基本思路,下面代码里就没有实现了。
动态规划存高度
暴力法的暴力之处在于每个元素都需要重新遍历和计算其左右两端的最大高度。这可以通过DP来优化,为每一个i存储其左端最大高度和右端最大高度,存储在leftMaxHeight和rightMaxHeight中,再用暴力的思路解。
一共需要遍历三次数组,前两次遍历用于求解和存储左右高度,第三次用于求解。
利用栈存储高度
建立一个栈,当idx对应元素高度不大于栈顶元素高度的时候,元素入栈。当栈不空且idx对应元素高度大于栈顶元素高度时,将所有小于idx高度的元素依次出栈,并进行计算。
例如序列[3, 2, 1, 1, 2, 3],当idx<4时,元素入栈;当idx=4时,两个元素“1”出栈并计算;当idx=5时,元素“2”出栈并计算。
由此可以在一次遍历过程内完成求解。
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
import java.util.Stack;
/**
*
* Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
* 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
*
*/
public class TrappingRainWater {
//从两端朝内逼近,每次更新较矮的端点minheight,在每次循环逼近过程中将小于minheight的差值即为可以接入的雨水
public int trap(int[] height) {
int begin = 0;
int end = height.length - 1;
int minheight = 0;
int result = 0;
while(begin < end){
//begin和end指针在移动到比minheight高的位置前,依次将元素值与minheight的差值加入雨水累计值
while(begin < end && height[begin] <= minheight){
result += minheight - height[begin];
begin++;
}
while(begin < end && height[end] <= minheight){
result += minheight - height[end];
end--;
}
//每次minheight更新为较矮的边
minheight = Math.min(height[begin], height[end]);
}
return result;
}
//同一思路另一算法:每次更新较矮的一边
public int trap1(int[] height) {
int begin = 0;
int end = height.length - 1;
int leftMaxHeight = 0;
int rightMaxHeight = 0;
int result = 0;
while(begin < end){
if(height[begin] < height[end]){
if(height[begin] >= leftMaxHeight){
leftMaxHeight = height[begin];
}else{
result += (leftMaxHeight - height[begin]);
}
++begin;
}
else{
if(height[end] >= rightMaxHeight){
rightMaxHeight = height[end];
}else{
result += (rightMaxHeight - height[end]);
}
--end;
}
}
return result;
}
//暴力法
//对于数组中的每一个元素,其容纳雨水最大水位等于两边杆的最大高度的最小值减去它自身的高度。
//动态规划
//为每一个i存储其左端最大高度和右端最大高度,存储在leftMaxHeight和rightMaxHeight中,再用暴力的思路解
public int trap2(int[] height) {
if(height.length == 0){
return 0;
}
//左端最大高度数组
int[] leftMaxHeight = new int[height.length];
leftMaxHeight[0] = height[0];
for(int i = 1; i < height.length; ++i){
leftMaxHeight[i] = Math.max(leftMaxHeight[i - 1], height[i]);
}
//右端最大高度数组
int[] rightMaxHeight = new int[height.length];
rightMaxHeight[height.length - 1] = height[height.length - 1];
for(int i = height.length - 2; i >= 0; --i){
rightMaxHeight[i] = Math.max(rightMaxHeight[i + 1], height[i]);
}
//对于数组中的每一个元素,其容纳雨水最大水位等于两边杆的最大高度的最小值减去它自身的高度。
int result = 0;
for(int i = 1; i < height.length - 1; i++){
result += Math.min(leftMaxHeight[i], rightMaxHeight[i]) - height[i];
}
return result;
}
//利用栈
//建立一个栈,当idx对应元素高度不大于栈顶元素高度的时候,元素入栈。当栈不空且idx对应元素高度大于栈顶元素高度时,将所有小于idx高度的元素依次出栈,并进行计算。
// 例如序列[3, 2, 1, 1, 2, 3],当idx<4时,元素入栈;当idx=4时,两个元素“1”出栈并计算;当idx=5时,元素“2”出栈并计算。
public int trap3(int[] height){
Stack<Integer> stack = new Stack<>();
int result = 0;
int idx = 0;
while(idx < height.length){
while(!stack.empty() && height[idx] > height[stack.peek()]){
int top = stack.pop();
if(stack.empty()){
break;
}
int distance = idx - stack.peek() - 1;
int boundHeight = Math.min(height[idx], height[stack.peek()]) - height[top];
result += distance * boundHeight;
}
stack.push(idx++);
}
return result;
}
}
#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#