当青训营遇上码上掘金
这里是接青豆一题的题解。
初次看到就觉得单调栈可以一试。单调栈求的是某个点之前第一个比它大/比它小的点。之前我总是喜欢往栈里存值,后来做得多了发现存值大概率浪费了这个栈,因为值是可以通过输入的数组O(1)求出的,所以我们往里面扔下标:
class Solution {
public: int trap(vector<int>& height) {
//这是一个单调递减栈
stack<int> st;
int n = height.size(),ans = 0;
for(int i = 0;i<n;i++) {
//last是上一个最小的柱子,刚被弹出,将要成为坑底
int last = -1;
//找到第一个大于等于i的,不满足的出栈
//这个出栈的过程中,被出栈的都可以和前面在栈中的元素+最新的这个柱子围成一个他们自己的坑来接青豆
//这个坑的大小就是下面这个公式(只计算和last的高度差的那部分是因为更低洼处已经在前面计算过了
while(!st.empty() && height[st.top()]<height[i]) {
if(last!=-1) ans+=(min(height[st.top()],height[i])-height[last])*(i-st.top()-1);
last = st.top();
st.pop();
}
//终于找到了一个更高的,那这个更高的还能和新来的整一个坑
if(!st.empty() && last!=-1) {
ans+=(min(height[st.top()],height[i])-height[last])*(i-st.top()-1);
}
//新来的入栈
st.push(i);
}
return ans;
}
};
时间复杂度O(n)
空间复杂度O(n)
进阶:能不能用常数空间解决?
能!
我们这样想:某个点处能不能接青豆全看两边能不能兜住,对于每一个列来说,只要左边有比它更高的,右边也有比它更高的,那它起码就能兜住1*(左边高度最大值和右边高度最大值中更小的那个 - 它的高度),因为只看它自己嘛。
既然下标 i 处能接的青豆量由leftMax[i] 和 rightMax[i] 中的最小值决定。由于数组 leftMax 是从左往右计算,数组 rightMax 是从右往左计算,因此可以使用双指针和两个变量代替两个数组。
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
int l = 0,r = n-1,leftMax = height[0],rightMax = height[n-1],ans = 0;
while(l<=r) {
//左边最大值更小就走左边,否则右边
if(leftMax <= rightMax) {
ans += leftMax - height[l++];
if(l>=n) break;
//加完青豆,更新l和max
leftMax = max(height[l],leftMax);
}else {
ans += rightMax - height[r--];
if(r<0) break;
rightMax = max(height[r],rightMax);
}
}
return ans;
}
};