_51LeetCode代码随想录算法训练营第五十一天-单调栈 | 503.下一个更大元素II、42.接雨水
题目列表
- 503.下一个更大元素II
- 42.接雨水
503.下一个更大元素II
代码随想录地址:https://programmercarl.com/0503.%E4%B8%8B%E4%B8%80%E4%B8%AA%E6%9B%B4%E5%A4%A7%E5%85%83%E7%B4%A0II.html
题目
给定一个循环数组 nums
( nums[nums.length - 1]
的下一个元素是 nums[0]
),返回 nums 中每个元素的 下一个更大元素 。
数字 x
的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1
。
示例 1:
输入: nums = [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
示例 2:
输入: nums = [1,2,3,4,3]
输出: [2,3,4,-1,4]
提示:
- 1 <= nums.length <= 1 0 4 10^4 104
- - 1 0 9 10^9 109 <= nums[i] <= 1 0 9 10^9 109
思路
把两个数组拼接在一起,然后使用单调栈求下一个最大值。
代码
/*
* @lc app=leetcode.cn id=503 lang=cpp
*
* [503] 下一个更大元素 II
*/
// @lc code=start
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
vector<int> res(nums.size(), -1);//存储结果数组
stack<int> st;
st.push(0);
for(int i = 1; i < nums.size() * 2; i++)
{
while(!st.empty() && nums[st.top()] < nums[i % nums.size()])
{
res[st.top()] = nums[i % nums.size()];
st.pop();
}
st.push(i % nums.size());
}
return res;
}
};
// @lc code=end
42.接雨水
这个题面试官喜欢问。
代码随想录地址:https://programmercarl.com/0042.%E6%8E%A5%E9%9B%A8%E6%B0%B4.html
题目
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例 2:
输入:height = [4,2,0,3,2,5]
输出:9
提示:
n == height.length
- 1 <= n <= 2 * 1 0 4 10^4 104
- 0 <= height[i] <= 1 0 5 10^5 105
思路
双指针思路
按列来计算,比较容易理解。
按列计算,每一列的宽度一定是1,就要求高度,实际上高度取决于 该列 左侧最高的柱子和右侧最高的柱子中最矮的那个柱子的高度。
如图:
该列雨水的高度为:min(lHeight, rHeight) - height
动态规划的思路
当前位置,左边的最高高度是前一个位置的左边最高高度和本高度的最大值。
即从左向右遍历:maxLeft[i] = max(height[i], maxLeft[i - 1]);
从右向左遍历:maxRight[i] = max(height[i], maxRight[i + 1]);
上面的结果用数组存起来,然后再使用下面的计算方式即可,这样就不需要重复计算了。
当前列雨水面积:min(左边柱子的最高高度,记录右边柱子的最高高度) - 当前柱子高度。
单调栈思路
单调栈是按行方向计算雨水。
- 单调栈内元素顺序
栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。
- 三种情况
情况1:
一旦发现添加的柱子高度大于栈头元素,就出现了凹槽,栈头元素是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。此时就要考虑出栈的事宜。
如图:
情况2:
遇到相同的元素,更新栈内下标,就是将栈里元素(旧下标)弹出,将新元素(新下标)加入栈中。
原因是,栈顶元素一定是当前元素的前一个元素,如果这两个元素相等了,那就一定不会形成凹槽。
情况3:
当前元素小于栈顶元素,直接入栈。
- 栈里要保存什么数值
通过 长 * 宽 来计算雨水面积的。
长就是通过柱子的高度来计算,宽是通过柱子之间的下标来计算。
只需要下标就够啦。
代码
双指针代码
- 时间复杂度为O(n^2)。
- 空间复杂度为O(1)。
由于时间复杂度会比较高,因此会超出时间限制。
/*
* @lc app=leetcode.cn id=42 lang=cpp
*
* [42] 接雨水
*/
// @lc code=start
class Solution {
public:
int trap(vector<int>& height) {
int all = 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 j = i + 1; j < height.size(); j++)
if(height[j] > rheight)
rheight = height[j];
for(int j = i - 1; j >= 0 ;j--)
if(height[j] > lheight)
lheight = height[j];
int h = min(lheight, rheight) - height[i];
//只有在h大于0时才加
if(h > 0) all += h;
}
return all;
}
};
// @lc code=end
动态规划代码
- 时间复杂度O(n)
- 空间复杂度O(n)
这个思路可以通过代码,但是呢打败的人数很少。
/*
* @lc app=leetcode.cn id=42 lang=cpp
*
* [42] 接雨水
*/
// @lc code=start
class Solution {
public:
int trap(vector<int>& height) {
if (height.size() <= 2) return 0;
vector<int> maxLeft(height.size(), 0);
vector<int> maxRight(height.size(), 0);
int size = maxRight.size();
// 记录每个柱子左边柱子最大高度
maxLeft[0] = height[0];
for (int i = 1; i < size; i++) {
maxLeft[i] = max(height[i], maxLeft[i - 1]);
}
// 记录每个柱子右边柱子最大高度
maxRight[size - 1] = height[size - 1];
for (int i = size - 2; i >= 0; i--) {
maxRight[i] = max(height[i], maxRight[i + 1]);
}
// 求和
int sum = 0;
for (int i = 0; i < size; i++) {
int count = min(maxLeft[i], maxRight[i]) - height[i];
if (count > 0) sum += count;
}
return sum;
}
};
// @lc code=end
单调栈思路
显然这三个里面单调栈是最优的啦
/*
* @lc app=leetcode.cn id=42 lang=cpp
*
* [42] 接雨水
*/
// @lc code=start
class Solution {
public:
int trap(vector<int>& height) {
//这样一定不会蓄水
if (height.size() <= 2) return 0;
int sum = 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();
st.push(i);
}
else
{//情况三,此时形成凹槽了
while(!st.empty() && height[i] > height[st.top()])
{
int mid = st.top();
st.pop();
//注意 这里一定要检查单调栈是否为空
if(!st.empty())
{
int high = min(height[i], height[st.top()]) - height[mid];
int length = i - st.top() - 1;
sum += high * length;
}
}
st.push(i);//将i入栈
}
}
return sum;
}
};
// @lc code=end