503.下一个更大元素 II
这题看上去有循环似乎不好做,其实还是蛮简单的。只需要遍历两遍来模拟循环即可,遍历两边还找不到结果的遍历N遍也照样找不到结果。单调栈写法仍然与 739.每日温度 相同
vector<int> nextGreaterElements(vector<int>& nums) {
stack<int> st;
vector<int> ans(nums.size(), -1);
st.push(0);
// 遍历两遍,保证每一个元素都能与其前面的元素进行对比
for (int i = 1; i < 2 * nums.size(); ++i) {
int index = i % nums.size();
if (nums[index] <= nums[st.top()])
st.push(index);
else {
while (!st.empty() && nums[index] > nums[st.top()]) {
ans[st.top()] = nums[index];
st.pop();
}
st.push(index);
}
}
return ans;
}
42.接雨水
这题首先得明白怎么计算一个柱子上的雨水量:
对于柱子i:
接雨水的宽度 = 1
(每个柱子占一格宽度)
接雨水的高度 = min(左边最高柱子高度, 右边最高柱子高度) - 第i根柱子高度
(高度的计算可以理解为一种水桶效应)
知道怎么计算雨水量了,那么最朴素的思路就是只要知道每一个柱子左右边最高柱子的高度各是多少就能计算出该柱子能接的雨水量了
可以使用双指针判断每一个柱子的左右最高高度,但那样对于每个i都要遍历一次数组,时间复杂度为O(n2)。所以使用空间换时间,使用两个数组分别记录当前位置左边与右边最高柱子的高度,时间复杂度为O(n)
优化的双指针写法:
int trap(vector<int>& height) {
// 使用两个数组分别记录当前位置左边与右边最高柱子的高度
vector<int> maxLeft(height.size(), 0);
vector<int> maxRight(height.size(), 0);
int ans = 0;
// 从左向右遍历第一遍,记录当前元素左边柱子最大值
maxLeft[0] = height[0];
for (int i = 1; i < height.size(); ++i) {
maxLeft[i] = std::max(maxLeft[i - 1], height[i]);
}
// 从右向左遍历第二遍,记录当前元素右边柱子最大值
maxRight[height.size() - 1] = height[height.size() - 1];
for (int i = height.size() - 2; i >= 0; --i) {
maxRight[i] = std::max(maxRight[i + 1], height[i]);
}
// 遍历第三遍,统计能接住的雨水量
for (int i = 1; i < height.size() - 1; ++i) {
int h = std::min(maxLeft[i], maxRight[i]) - height[i];
// h为负说明当前柱子是墙壁,不计入
if (h > 0)
ans += h;
}
return ans;
}
单调栈写法:
既然要找左右第一个比当前高的柱子,那么就很容易联想到单调栈。由于是找比自身大的,所以栈顶到栈底应该是递增的。对于数组中的每一个元素,有以下三种情况:
- 情况1——当前元素小于栈顶元素:当前元素直接入栈
- 情况2——当前元素等于栈顶元素:当前元素替换栈顶元素(这步的作用是在情况3中节省计算开销,可以选择省略)
- 情况3——当前元素大于栈顶元素:弹出栈顶,计算能接的雨水:
- 先弹出栈顶,计算这个弹出的元素能接的雨水量
- 计算高度 h,height[i]是右边第一个高比其大的柱子高度,height[st.top()]是左边第一个高比其大的柱子高度,根据木桶效应选较低的一个
- 计算长度 l,长度 = i - st.top() - 1(注意此时的st.top()是完成过一次弹出操作的栈顶元素)
- 按行计算雨水量:ans += h * l
重点是理解按行计算雨水量:
int trap(vector<int>& height) {
stack<int> st;
int ans = 0;
st.push(0);
for (int i = 1; i < height.size(); ++i) {
// 分三种情况:
// 情况1——当前元素 小于 栈顶元素:当前元素入栈
if (height[i] < height[st.top()])
st.push(i);
// 情况2——当前元素 等于 栈顶元素:当前元素替换栈顶元素
else if (height[i] == height[st.top()]) {
st.pop();
st.push(i);
}
// 情况3——当前元素 大于 栈顶元素:弹出栈顶,计算能接的雨水
else {
while (!st.empty() && height[i] > height[st.top()]) {
int bottom = height[st.top()];
st.pop();
if (st.empty())
break;
// 计算高度,height[i]是右边第一个高比其大的柱子高度,height[st.top()]是左边第一个高比其大的柱子高度
int h = std::min(height[i], height[st.top()]) - bottom;
// 计算长度
int l = i - st.top() - 1;
// 按行累计雨水量
ans += h * l;
}
st.push(i);
}
}
return ans;
}