下一个更大元素II
这道题与昨天的题相比,数组是循环数组。我们可以用下标取余的方法来解决循环数组问题。
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
int n = nums.size();
vector<int> result(n, -1);
stack<int> st;
for(int i = 0; i < 2 * n; i++) {
// 当前元素比栈顶元素大,栈顶元素出栈,并记录结果
while(!st.empty() && nums[i % n] > nums[st.top()]) {
result[st.top()] = nums[i % n];
st.pop();
}
st.push(i % n);
}
return result;
}
};
接雨水
暴力解法:
从图象上看,按列来遍历,计算每一个柱子能接的雨水量。具体做法就是从当前的柱子出发,分别寻找左侧最高的柱子和右侧最高的柱子。当前柱子能接的雨水取决于左右最高柱子的最小值。
class Solution {
public:
int trap(vector<int>& height) {
int result = 0;
for(int i = 0; i < height.size(); i++) {
if(i == 0 || i == height.size() - 1) continue;
int rheight = height[i];
int lheight = height[i];
for(int r = i + 1; r < height.size(); r++) {
if(rheight < height[r]) {
rheight = height[r];
}
}
for(int l = i - 1; l >= 0; l--) {
if(lheight < height[l]) {
lheight = height[l];
}
}
result += min(rheight, lheight) - height[i];
}
return result;
}
};
双指针优化:
以上暴力解法会超时。我们可以提前记录每个位置左侧的最高柱子和右侧最高柱子,记录在 maxLheight 和 maxRheight 数组中。
class Solution {
public:
int trap(vector<int>& height) {
int result = 0;
vector<int> maxLeft(height.size(), 0);
vector<int> maxRight(height.size(), 0);
maxLeft[0] = height[0];
for(int i = 1; i < height.size(); i++) {
maxLeft[i] = max(height[i], maxLeft[i - 1]);
}
maxRight[height.size() - 1] = height[height.size() - 1];
for(int i = height.size() - 2; i >= 0; i--) {
maxRight[i] = max(height[i], maxRight[i + 1]);
}
for(int i = 1; i < height.size() - 1; i++) {
result += min(maxLeft[i], maxRight[i]) - height[i];
}
return result;
}
};
单调栈解法:
这道题也可以按照找到接雨水的凹槽的想法来做,从图象上看类似于按行遍历的做法。单调栈一般用于一维数组中找到每个元素的下一个更大或更小的元素,而栈中还有单调的顺序,所以很适合用来寻找能接雨水的凹槽。
- 我们维护一个单调递增栈(从栈头到栈底),每当当前元素大于栈顶元素时,当前元素、栈顶元素、栈头第二个元素组成了一个凹槽,我们需要计算这个凹槽的容量。首先就是确定能凹槽能接水的高度,这取决于左右两个高柱子中更低的那个,其次需要确定宽度,这由栈所记录的元素下标差来确定。这样我们就处理了一个凹槽。
- 当前元素小于栈顶元素时,符合递增栈条件,直接入栈。
- 当前元素等于栈顶元素时,此时需要弹出栈顶元素,将当前元素下标入栈,因为最后计算下标差,肯定是要计算相等柱子中靠右的那个与当前下标的差,这部分才能承接雨水。其实都入栈也是可以的,只是遇到相同高度只能加0。
class Solution {
public:
int trap(vector<int>& height) {
int result = 0;
stack<int> st;
st.push(0);
for(int i = 1; i < height.size(); i++) {
if(height[i] < height[st.top()]) {
st.push(i);
}
else if(height[i] == height[st.top()]) {
st.pop(); // 其实也可以不弹出,在后续累计雨水时,等高的柱子只会累计0,不会有影响
st.push(i);
}
else {
while(!st.empty() && height[i] > height[st.top()]) {
int mid = st.top(); // 取凹槽低的柱子高度
st.pop();
if(!st.empty()) { // 注意判断一下栈是否空了,空了证明左侧没有柱子能形成凹槽了
int h = min(height[i], height[st.top()]) - height[mid];
int w = i - st.top() - 1;
result += h * w;
}
}
st.push(i);
}
}
return result;
}
};