题目描述
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

方法一: 单调栈,
时间复杂度O(n),其中 n 是数组height 的长度。从 0 到 n-1 的每个下标最多只会入栈和出栈各一次。
空间复杂度O(n)其中 n是数组height 的长度。空间复杂度主要取决于栈空间,栈的大小不会超过 n。
C++
class Solution {
/*
思路:类似于前边做过的求柱子构成的矩形的最大面积
构造一个从底部到顶部单调递减的栈,当遍历的一个柱子比栈顶柱子高的时候就会构成一个凹槽,每次加上min(当前遍历柱子,栈顶柱子的前一个柱子)*两者索引距离。
遍历到的柱子大于栈顶的柱子,就构成凹槽了,注意出栈的时候将相同的栈顶元素也一起出栈
单调递减栈:从底到高单调递减
*/
public:
int trap(vector<int>& height) {
//注意特殊情况
if(height.size()==0){
return 0;
}
int sum=0;
stack<int> sta;
for(int i=0;i<height.size();i++){
while(!sta.empty() && height[i]>height[sta.top()]){
int cur=sta.top();
//将栈顶元素出栈,并且把所有和栈顶元素相同的也一起出栈
sta.pop();
while(!sta.empty() && height[sta.top()]==height[cur]){
sta.pop();
}
if(!sta.empty())//注意这里
sum+=(min(height[i],height[sta.top()])-height[cur])*(i-sta.top()-1);
}
sta.push(i);
}
return sum;
}
};
JAVA
单调栈
class Solution {
public int trap(int[] height) {
//单调栈
if(height.length==0)
return 0;
Stack<Integer> stack=new Stack<Integer>();
int res=0;
for(int i=0;i<height.length;i++){
while(!stack.isEmpty() && height[i]>height[stack.peek()]) {
int cur=stack.peek();
stack.pop();
while(!stack.isEmpty() && height[stack.peek()]==height[cur]){
stack.pop();
}
if(!stack.isEmpty()){
res=res+(Math.min(height[i],height[stack.peek()])-height[cur])*(i-stack.peek()-1);
}
}
stack.push(i);
}
return res;
}
}
方法2:动态规划
总共能接的雨水就是以每个柱子为底可以接雨水的和。 对于每个柱子i,可以接的雨水是柱子两边最高值的最小值减去heaght[i]。 所以,对于遍历,需要对每个柱子向左右两边进行扫描最大值,所以时间复杂度为O(n^2).
所以如果知道了每个柱子的两边最大值,时间复杂度就是O(n). 使用动态规划可以实现。
设置两个数组,leftMax,rightMax分别存放每个柱子的左右两边的最大值,leftMax[i]表示第i个柱子的左边的最大值。
填完leftmax和rightmax表之后,每个柱子i处可以接的雨水为
min(leftmax[i],rightmax[i])-height[i]
时间复杂度:O(n)
空间复杂度:O(n)
class Solution {
public int trap(int[] height) {
//动态规划
int n=height.length;
if(n==0) return 0;
int [] leftMax=new int[n];
int [] rightMax=new int[n];
//初始化
leftMax[0]=height[0];
rightMax[n-1]=height[n-1];
//填表
for(int i=1;i<n;i++){
leftMax[i]=Math.max(leftMax[i-1],height[i]);
}
for(int i=n-2;i>=0;i--){
rightMax[i]=Math.max(rightMax[i+1],height[i]);
}
int res=0;
for(int i=0;i<n;i++){
res=res+Math.min(leftMax[i],rightMax[i])-height[i];
}
return res;
}
}
方法三: 双指针
优化动态规划的空间复杂度为O(1)
leftMax填表的时候是从左向右填,rightMax填表的时候是从右向左填
所以设置指针 left,right分别从左向右,从右向左,然后设置两个变量leftMax,rightMax,一边移动一边计算。 左右两个指针会和时就计算完了。
T(n)=O(n) , S(n)=O(1)
class Solution {
public int trap(int[] height) {
//双指针法
if(height.length==0)
return 0;
int left=0;
int right=height.length-1;
int leftMax=0;
int rightMax=0;
int res=0;
while(left<right){
leftMax=Math.max(leftMax,height[left]);
rightMax=Math.max(rightMax,height[right]);
if(height[left]<height[right]){
res+=leftMax-height[left];
++left;
}
else{
res+=rightMax-height[right];
--right;
}
}
return res;
}
}
1837

被折叠的 条评论
为什么被折叠?



