代码随想录算法训练营第五十九天| 单调栈 503 下一个更大元素II 42 接雨水

news/2024/12/22 0:03:08/

代码随想录算法训练营第五十九天| 单调栈 503 下一个更大元素II 42 接雨水

LeetCode 503 下一个更大元素II

题目: 503.下一个更大元素II

思路:本题重点在于如何处理循环数组,首先想到将两个数组拼在一起,然后使用单调栈求下一个最大值

方法一:

class Solution {
public:vector<int> nextGreaterElements(vector<int>& nums) {//拼接新的numsvector<int> nums1(nums.begin(), nums.end());nums.insert(nums.end(), nums1.begin(), nums1.end());//用新的nums大小来初始化resultvector<int> result(nums.size(), -1);if(nums.size() == 0) return result;//开始单调栈stack<int> st;st.push(0);for(int i = 1; i < nums.size(); i++) {if(nums[i] < nums[st.top()]) st.push(i);else if (nums[i] == nums[st.top()]) st.push(i);else {while(!st.empty() && nums[i] > nums[st.top()]) {result[st.top()] = nums[i];st.pop();}st.push(i);}}//最后把result数组resize到原数组大小result.resize(nums.size() / 2);return result;}
};

注:此方法比较直观,但是做了无用操作,修改了nums数组,最后还需result数组resize,resize时间复杂度为O(1),但是扩充nums数组多了一个O(n)的操作。

方法二:也可以不用扩充nums,在遍历过程中模拟走两遍nums

class Solution {
public:vector<int> nextGreaterElements(vector<int>& nums) {vector<int> result(nums.size(), -1);if(nums.size() == 0) return result;stack<int> st;st.push(0);for(int i = 1; i < nums.size() * 2; i++) {//模拟遍历两遍nums,注意都是用i % nums.size()来操作if(nums[i % nums.size()] < nums[st.top()]) st.push(i % nums.size());else if(nums[i % nums.size()]  == nums[st.top()]) st.push(i % nums.size());else {while(!st.empty() && nums[i % nums.size()] > nums[st.top()]) {result[st.top()] = nums[i % nums.size()];st.pop();}st.push(i % nums.size());}}return result;}
};

LeetCode 42 接雨水

题目: 42.接雨水

接雨水问题在面试中非常常见,通常有三种解法:

  • 双指针法
  • 动态规划
  • 单调栈

双指针法,也是暴力解法:需要明确是按照行来计算还是按照列来计算,这里考虑按照列来计算,宽度一定为1,求出每一列雨水的高度即可。

每列雨水的高度取决于左侧最高的柱子和右侧最高的柱子中最矮的柱子高度,从头遍历所有列,相加则可得到总雨水的体积,注意第一个柱子和最后一个柱子不接雨水

for(int i = 0; i < height.size(); i++) {//第一个柱子和最后一个柱子不接雨水if(i == 0 || i == height.size() - 1) continue;
}

在for循环中求左右两边最高柱子,代码为:

int rHeight = height[i];  //记录右边柱子的最高高度
int lHeight = height[i];  //记录左边柱子的最高高度
for(int r = i; r < height.size(); r++) {if(height[r] > rHeight) rHeight = height[r];
}
for(int l = i - 1; l >= 0; l--) {if(height[l] > lHeight) lHeight = height[l];
}

最后计算该列雨水高度

int h = min(lHeight, rHeight) - height[i];
if(h > 0) sum += h;  //注意只有h大于零的时候,再统计到总和中

完整代码:

class Solution {
public:
int trap(vector<int>& height) {int sum = 0;for(int i = 0; i < height.size() - 1; 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(height[r] > rHeight) rHeight = height[r];}for(int l = i - 1; l >= 0; l--) {if(height[l] > lHeight) lHeight = height[r];}int h = min(lHeight, rHeight) - height[i];if(h > 0) sum += h;}return sum;
}
};

双指针优化:为了得到两边最高高度,使用双指针遍历,每到一个柱子都向两边遍历一遍,有重复计算。把每一个位置的左边最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight),从而避免重复计算。

当前位置,左边的最高高度是前一个位置的左边最高高度和本高度的最大值。

从左向右遍历:maxLeft[i] = max(height[i], maxLeft[i - 1]);

从右向左遍历:maxRight[i] = max(height[i], maxRight[i + 1]);

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;}
};

单调栈解法:

接雨水需要寻找一个元素,右边最大元素以及左边最大元素,来计算雨水面积。

  • 首先单调栈按照行方向来计算雨水

  • 使用单调栈内元素的顺序

从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。

一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。

  • 如果遇到相同高度的柱子

遇到相同的元素,更新栈内下标,将栈里元素(旧下标)弹出,将新元素(新下标)加入栈中。。

如果遇到相同高度的柱子,需要使用最右边的柱子来计算宽度

  • 栈里保存数值

使用单调栈,通过长 * 宽计算雨水面积。长是通过柱子的高度来计算,宽是通过柱子之间的下标来计算,

栈里有没有必要存一个pair<int, int>类型的元素,保存柱子的高度和下标呢。不需要,栈里就存放下标就行,想要知道对应的高度,通过height[stack.top()] 就知道弹出的下标对应的高度了。

完整代码:

class Solution {
public:int trap(vector<int>& height) {if (height.size() <= 2) return 0; // 可以不加stack<int> st; // 存着下标,计算的时候用下标对应的柱子高度st.push(0);int sum = 0;for (int i = 1; i < height.size(); i++) {if (height[i] < height[st.top()]) {     // 情况一st.push(i);} if (height[i] == height[st.top()]) {  // 情况二st.pop(); // 其实这一句可以不加,效果是一样的,但处理相同的情况的思路却变了。st.push(i);} else {                                // 情况三while (!st.empty() && height[i] > height[st.top()]) { // 注意这里是whileint mid = st.top();st.pop();if (!st.empty()) {int h = min(height[st.top()], height[i]) - height[mid];int w = i - st.top() - 1; // 注意减一,只求中间宽度sum += h * w;}}st.push(i);}}return sum;}
};

http://www.ppmy.cn/news/46861.html

相关文章

打造安全无忧软件应用的十大最佳实践

安全无忧的软件开发最佳实践实在是很有必要&#xff0c;因为安全风险无处不在。在网络攻击盛行的时代&#xff0c;它们可能影响到每个人&#xff0c;包括个人、公司和政府。因此&#xff0c;确保软件开发的安全性至关重要。 本篇文章将解释了什么是安全的软件&#xff0c;如何…

[SSD核心技术:FTL 17] 固态硬盘掉电保护也是一门艺术 | 掉电保护原理与抉择 | 掉电保护测试

声明 主页:元存储的博客_CSDN博客 依公开知识及经验整理,如有误请留言。 个人辛苦整理,付费内容,禁止转载。 内容摘要 全文4800字, 阅读大约 24 分钟 前言 1 系统掉电的灾难

一起学 WebGL:三角形加上渐变色

大家好&#xff0c;我是前端西瓜哥。之前教大家绘制一个红色的三角形&#xff0c;这次我们来画个有渐变的三角形。 本文为系列文章&#xff0c;请先阅读如何绘制红色三角形的文章&#xff1a; 《一起学 WebGL&#xff1a;绘制三角形》 原来的写法&#xff0c;颜色是在片元着色器…

恢复误删文件

误删恢复 用losf恢复进程存在的文件 注意此处要后台进程存在 创建一个文件&#xff0c;用tail命令&#xff0c;模拟文件一直被监听 打开另外一个终端&#xff0c;删除这个文件 用lsof命令查看被删除的文件&#xff0c;可以发现文件虽然被删除&#xff0c;但是进程依然在 然后…

16 个优秀的 Vue 开源项目

为什么我们要关注Vue Vue是一个用于构建用户界面的JavaScript框架。值得关注的是&#xff0c;它在没有谷歌和Facebook的支持下获得了大量的人气。 Vue是结合react和angular的最好的方法&#xff0c;并且拥有一个有凝聚力的&#xff0c;活跃的&#xff0c;能够应对开发问题的大型…

2023/4/13总结

最小生成树 一、Prim算法 1.prim算法也被称为“加点法”&#xff0c;因为该算法是先从任意一顶点出发不断的选择目前距离最近且未被选择的点加入到已选的集合中&#xff0c;直到所有的点都被选到。&#xff08;和最短路径中的Dijkstra算法很像&#xff09; 2.prim算法的实现…

文件操作【下篇】

文章目录 &#x1f5c3;️5.文件的随机读写&#x1f4c1;5.1. fseek&#x1f4c1;5.2. ftell&#x1f4c1;5.3. rewind &#x1f5c3;️6.文本文件和二进制文件&#x1f5c3;️7.文件读取结束的判定&#x1f4c1;7.1. 被错误使用的 feof &#x1f5c3;️8.文件缓冲区 &#x1f…

折叠屏市场起风,华为、OPPO“你追我赶”

配图来自Canva可画 现如今&#xff0c;智能手机已经成为了人们生活中不可或缺的重要工具&#xff0c;无论是出行&#xff0c;还是社交&#xff0c;亦或是支付&#xff0c;只需要一部智能手机就可以通通搞定。因此&#xff0c;在消费者多样化需求的助推下&#xff0c;智能手机行…