503.下一个更大元素Ⅱ
本题与 496.下一个更大元素Ⅰ 如出一辙,求在给定一维数组中,每个元素右起第一个更大元素。不同之点在于,本题要求将一维数组视为首尾相连的,最后一个元素的下一个元素是第一个元素。问题的重点在于 如何使用单调栈处理循环数组。
方法一:将原数组扩容到原来的两倍,即将 nums 拼接成 nums + nums,这样 nums[nums.size() - 1] 后面的元素就是 nums[0],数组在物理和逻辑上都变为循环的。然后,使用一个单调栈求解每个元素的下一个更大元素即可。
class Solution {
public:
vector<int> nextGreaterElements(vector<int> &nums) {
vector<int> nums1(nums.begin(), nums.end());
nums.insert(nums.end(), nums1.begin(), nums1.end());
vector<int> result(nums.size(), -1);
stack<int> st;
for (int i = 0; i < nums.size(); ++i) {
while (!st.empty() && nums[i] > nums[st.top()]) {
result[st.top()] = nums[i];
st.pop();
}
st.push(i);
}
result.resize(nums.size() / 2);
return result;
}
};
在上述代码中,我们将 nums数组的内容和长度扩容为原来的两倍。使用一个长度为扩容完后的 nums.size() 的容器作为结果集 result,在使用单调栈更新 result 后,需要将结果集 result resize 至原来的一半,因为在 result 中有一半的内容是重复的。
方法二:事实上,扩容 nums 是为了能够在逻辑上使 nums[nums.size()] 和 nums[0] 相邻,而不必在物理上使元素也相邻,我们只要在遍历至末尾时,能够重新开始二次遍历 nums,即模拟走了两边数组。
class Solution {
public:
vector<int> nextGreaterElements(vector<int> &nums) {
int len = nums.size();
vector<int> result(len, -1);
stack<int> st;
for (int i = 0; i < len * 2; ++i) {
while (!st.empty() && nums[i % len] > nums[st.top()]) {
result[st.top()] = nums[i % len];
st.pop();
}
st.push(i % len);
}
return result;
}
};
在上述代码中,在 for 循环中遍历两倍长度的数组,使用 i % nums.size() 更新 result,事实上,单调栈中,nums 中的每个元素被压栈两次,result 结果集中的每个元素被更新了两次,但是每次的更新内容是一样的。这样空间复杂度减少为 O(1),没有使用到额外的空间,减少代码冗余。
42.接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。很复杂,我只能说很复杂。我们求每根柱子能装水的多少,然后将数量累加,即为给定数组能够装水的数量。每根柱子装水的容量,为以当前柱子为底,以两边最近的更高柱子为边形成的凹槽的容量。根据木桶的短板原理,木桶能装水的多少取决于最短的板子的长度,在本题中,一个凹槽能装水的多少取决于两个相邻的更高的柱子短的那一个。
借助 代码随想录中对应章节的图描述,1 号柱子的容量为 0,其左边没有比其高的柱子,无法形成凹槽;2 号柱子的高度为 0,容量为 1,其右边相邻更高的柱子为 3 号,高度为 2,左边相邻更高柱子为 1号,高度为 1。此时,1,2,3 号柱子形成一个凹槽,凹槽的容量为 1 号柱子(比 3 号柱子矮) - 2 号的高度,1 - 0 = 1,此时说的容量其实为高度,还需要求得宽度,宽度为右边柱子的索引 - 左边柱子的索引 - 1,3 - 1 - 1 = 1,所以 2 号柱子的容量为 1 * 1 = 1。依次类推,计算后续每个柱子的容量,从图中可以看出,不是每个柱子都能形成凹槽,存在一些柱子的容量为 0。
如何将上述过程转换为单调栈,其实就是求数组中每个元素其相邻右边第一个更大元素,和相邻左边第一个更大元素。借助一个单调递增栈,能够求当前元素右边第一个更大元素,此时栈顶元素为当前元素,将栈顶元素出栈之后,新的栈顶元素,即为当前元素左边第一个更大元素。
class Solution {
public:
int trap(vector<int> &height) {
int len = height.size();
int result = 0;
stack<int> st;
for (int i = 0; i < len; ++i) {
while (!st.empty() && height[i] > height[st.top()]) {
int index = st.top();
st.pop();
if (!st.empty()) {
int h = min(height[i], height[st.top()]) - height[index];
int w = i - st.top() - 1;
result += h * w;
}
}
st.push(i);
}
return result;
}
};