方法一:栈
需要画图体验。
当前高度不大于栈顶入栈;
当前高度大于栈顶时,栈顶出栈,此时出栈的值最小,相当于桶底。比较当前高度和新的栈顶高度,它们相当于桶壁,较小值和出栈值的差为盛水高度。
需要注意的是,如果出栈后栈空则直接返回,因为一个桶壁是不能盛水的;并且需要考虑两个桶壁的距离。
class Solution {
public:
int trap(vector<int>& height) {
int res=0;
stack<int> st;
int i=0; //i为数组下标
while(i<height.size()){
while(!st.empty() && height[i]>height[st.top()]){//如果栈不为空,并且当前高度大于栈顶高度
int n=height[st.top()]; //保存栈顶高度,并出栈
st.pop();
if(st.empty()) //如果此时栈空直接返回
break;
int distance=i-st.top()-1; //计算当前下标和弹出栈顶下标的距离
res+=distance * (min(height[st.top()],height[i]) - n); //计算盛水量
}
st.push(i++); //如果栈空或者当前高度小于栈顶高度,入栈,下标后移
}
return res;
}
};
方法二:双指针
class Solution {
public:
int trap(vector<int>& height) {
if(height.size()<2) return 0;
int res=0;
int maxleft=0,maxright=0; //左右边最大高度
int left=1,right=height.size()-2; //左右指针
for(int i=1;i<height.size()-1;i++){
//从左到右更新
if(height[left-1]<height[right+1]){
maxleft=max(maxleft,height[left-1]);
if(maxleft>height[left])
res+= maxleft-height[left];
++left;
}
//从右到左更新
else{
maxright=max(maxright,height[right+1]);
if(maxright>height[right])
res+= maxright-height[right];
--right;
}
}
return res;
}
};
方法三:按行计算
每行计算,在第n行时,如果当前高度小于n并且有两边高度大于等于n的,说明这个地方有水。用一个tmp变量保存当前层累积量,当第一次出现大于等于n的高度时,tmp开始更新:如果遇到小于n的数,tmp加1;如果遇到大于等于n的数,则tmp加入到总数res中并将tmp清零。
这种方法会超时!
class Solution {
public:
int trap(vector<int>& height) {
int max=0;
for(int i=0;i<height.size();i++){ //求出最高行
if(height[i]>max)
max=height[i];
}
int res=0;
for(int i=1;i<=max;i++){
bool first=true; //用于判断是否更新tmp
int tmp=0;
for(int j=0;j<height.size();j++){
if(height[j]<i && first) //没出现
continue;
first=false; //第一次出现
if(height[j]<i) //开始更新
++tmp;
else{
res+=tmp;
tmp=0;
}
}
first=true;
tmp=0;
}
return res;
}
};
方法四:按列求
两边不用考虑,判断当前高度与左右两边最大高度的较小值的大小,如果小于较小值则差值为当前列的盛水量。
时间复杂度:O(n²)
空间复杂度:O(1)
class Solution {
public:
int trap(vector<int>& height) {
if(height.size()<2) return 0;
int res=0;
for(int i=1;i<height.size()-1;i++){ //左右两边都不用考虑,不会有水
int max_left=0; //左边最大值
for(int j=0;j<i;j++){
if(height[j]>max_left)
max_left=height[j];
}
int max_right=0; //右边最大值
for(int j=i+1;j<height.size();j++){
if(height[j]>max_right)
max_right=height[j];
}
if(height[i]<min(max_left,max_right)) //如果当前高度小于两边最大中较小的
res+=min(max_left,max_right)-height[i]; //差值为盛水量
}
return res;
}
};
方法五:动态规划
优化方法四,用两个数组来保存左边最大高度和右边最大高度。
时间复杂度:O(n)
空间复杂度:O(n)
class Solution {
public:
int trap(vector<int>& height) {
if(height.size()<2) return 0;
int res=0;
int max_left[height.size()]={0}; //表示左边最大值
int max_right[height.size()]={0}; //表示右边最大值
for(int i=1;i<=height.size()-1;i++) //i从第二个开始
max_left[i]=max(max_left[i-1],height[i-1]);
for(int i=height.size()-2;i>=0;i--) //i从倒数第二个开始
max_right[i]=max(max_right[i+1],height[i+1]);
for(int i=1;i<height.size()-1;i++){ //左右两端都不用考虑,不会有水
if(height[i]<min(max_left[i],max_right[i])) //如果当前高度小于两边最大中较小的
res+=min(max_left[i],max_right[i])-height[i]; //差值为盛水量
}
return res;
}
};