单调栈:(C++)

在题目的要求中,存在先进后出(即在前面的数据需要遍历到后面的某一数据时才能确定计算值)单调栈在一部分解题场景中避免了暴力解法的高时间复杂度问题,但是在做题过程中视情况而定,有些题目的最优解不一定使用单调栈,需要灵活解题,多思考一下或许有更加简便的算法和逻辑。

LeetCode 经典题目:

1. 「力扣」第84: 柱状图中最大的矩形

算法思想:

创建一个保存元素下标的 整形栈,遍历数组依次入栈:

  1. 当前值比栈顶元素大时,入栈当前元素下标
  2. 当前值比栈顶元素小时,说明栈顶元素此时可以判断面积了->出栈
  3. 需要注意的是出栈后的栈顶元素判断区间(宽)= 当前数组的的下标i与现在的栈顶元素的距离-1
  4. 出栈的栈顶元素对应的数组值为(高)
  5. 在首位添加哨兵(值为0的元素),在下标处理计算、首尾判断时无需特殊处理(类似链表头结点删除),更为简便

class Solution {
public:
int largestRectangleArea(vector<int>& heights) {stack<int>s;int len = heights.size() + 2;vector<int>h2;h2.push_back(0);//头哨兵for(int i = 0;i< heights.size(); i++){h2.push_back(heights[i]);//复制中间元素}h2.push_back(0);//尾哨兵//转换新的数组(首尾哨兵结点)int area = 0;s.push(0);//头哨兵入栈,便于计算间距for (int i = 1; i < h2.size(); i++){if (h2[i] >= h2[s.top()])s.push(i);else{while (h2[i] < h2[s.top()]){int tmp = s.top();s.pop();int a = (i - s.top() - 1) * h2[tmp];area = area < a ? a : area;}i--;//当前的i 作用于出栈计算之前不能确定的面积,//但当所有符合条件的下标出栈后,i下标对应的栈顶元素也重新对应,//应当再次对i是否入栈做判断,i--再给一次机会}}return area;
}
};

2. 「力扣」第42题:接雨水

算法思想:

  1. 用两侧的高墙才能将水围住,只需要找到比当前墙更高或一样高的另一面墙才能计算出之间的水量,(间距*墙高-中间的矮墙),得到这一区间的水量,下标从后一面墙继续向结尾遍历
  2. 需要注意的是上面的设想存在一种情况,当前面的墙高于后面的所有墙,则无法判断,所以在确认以第一面墙的标准的时候,需要向后寻找最高的墙是否存在高于当前的墙,如果没有,则将第一面墙的高度降低为后续最高墙一致的高度,确保算法无遗漏
  3. 具体实现:
    1. 创建栈保存元素下标,当后续元素小于栈顶元素时(中间低墙,直接跳过)
    2. 遍历的元素大于或等于栈顶元素时,此时可以确定中间的积水区域,当前(下标i - 栈顶元素下标)*栈顶元素的数值-区间中间的元素”低墙 “,不断累加
    3. 计算积水区域结束后, 后一面墙若不是最后一个元素,则入栈(并作最大值判断)

算法思想2:

数组从左向右计算该元素后可以累计的积水量,再从右向左重复判断,两次的结果取最小值累加为结果

class Solution {
public:
int trap(vector<int>& height) {stack<int>s;int sum = 0;s.push(0);if(height.size()>1){int max = *max_element(height.begin() + s.top() + 1, height.end());if (height[0] > max)height[0] = max;}for (int i = 1; i < height.size(); i++){if (height[i] >= height[s.top()]) {sum += (i - s.top() - 1) * height[s.top()];for (int j = s.top() + 1; j < i; j++)sum -= height[j];s.pop();if(i!=height.size()-1){s.push(i);int max0 = *max_element(height.begin() + s.top() + 1, height.end());if (height[i] > max0)height[i] = max0;}}}return sum;
}
};

3. 「力扣」第739题:每日温度

算法思想:

创建栈保留数组下标

  1. 入栈0号下标,用作第一次元素值判断
  2. 当遍历的元素值大于栈顶元素时,计算间距保存到数组并出栈
  3. 若遍历元素小于栈顶元素时,入栈,后序遍历过程中才能得到答案
  4. esay 的实现了
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {vector<int>v(temperatures.size());stack<int>s;s.push(0);int i;for (i = 1; i < temperatures.size(); i++){if (temperatures[i] > temperatures[s.top()]){while (s.size() > 0 && (temperatures[i] > temperatures[s.top()])){v[s.top()] = i - s.top();s.pop();}s.push(i);}elses.push(i);}return v;
}
};

4. 「力扣」第496题:下一个更大元素

算法思想:

  1. 将nums2的元素从后向前遍历,顺序入栈,且始终保持栈内元素递减,则栈内元素的下一元素为所求的nums2 的下一更大元素
  2. 当栈内为空,直接入栈;
  3. 栈不为空,当前元素大于栈顶元素,出栈,保持栈内顺序递减
  4. 在保持当前新元素入栈后栈内元素依旧递减的状态(此时新元素未入栈)
    1. 当栈为空,说明nums2不存在下一更大元素
    2. 当栈不为空,则栈顶为所求的下一更大元素
  1. 这些所求信息用哈希表存储,key为所求元素,value为下一更大元素
  2. 在所有信息都保存在哈希表的基础上,只需查询map[key]对应的值返回结果即可
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {//单调栈vector<int>v(nums1.size(), -1);unordered_map<int, int>hashmap;stack<int>s;for (int i = nums2.size() - 1; i >= 0; i--){if (s.empty()!=1&&nums2[i] > s.top()){while (s.empty() != 1 && nums2[i] > s.top()){s.pop();}}hashmap[nums2[i]] = s.empty() ? -1 : s.top();s.push(nums2[i]);}for (int i = 0; i < nums1.size(); i++){v[i] = hashmap[nums1[i]];}return v;}
};

5. 「力扣」第316题:去除重复字母

class Solution {
public:
string removeDuplicateLetters(string s) {unordered_map<char, int>map;unordered_map<char, bool>b;for (int i = 0; i < s.size(); i++){map[s[i]]++;b[s[i]] = 0;}string s2 = "";for (auto it=s.begin();it!=s.end();it++  ){if (b[*it] == 0)//如果栈内不存在{while (!s2.empty() && s2.back() > *it&& map[s2.back()] >0)//如果栈头大于 it{b[s2.back()] = 0;s2.pop_back();}s2.push_back(*it);b[*it] = 1;}map[*it]--;//所有it在循环遍历时依次--;与是否入栈无关}return s2;
}
};

谁说用单调栈解题就一定要用栈stack当容器了? string不行吗?

算法思想:

这道题解决两个问题:删除重复项、字典序排列!

  1. 用哈希表unordered_map1<char,int>实现,键值对键存数据,value存次数
  2. 第二个问题需要结合第一个问题的哈希表实现排列:

哈希表2unordered_map1<char,bool> value 值为0,代表是否当前元素已在栈中入栈value为1,出栈后改为0;

  1. 字典序排列:
    1. 当遍历的当前元素不是重复元素时则直接入栈(map1[key]==1时)
    2. 当遍历的当前元素小于栈顶元素的字典序时,并且栈顶元素为重复元素时:出栈入新元素(!s2.empty() && s2.back() > *it&& map[s2.back()] >0)
    3. 当遍历到栈内存在的元素时(map2[]==1;),跳过,因为栈内已经是最优解
    4. 在每次遍历元素结束后,将该元素的map1计数器--

6. 「力扣」第402题:移掉K位数字

算法思想:

  1. 从数组开头向后遍历的过程中入栈,必须保持入栈的数据保持递增
  2. 若后面入栈的数据小于栈内栈顶元素,则出栈直到入栈新数据保持所有数据有序
  3. 每次出栈则代表删除一个元素,移除的计数器加加
  4. 若计数器在未遍历完所有数组元素时已经归零,则直接将剩余的数组元素入栈
  5. 若遍历完所有数组元素后,移除的计数器依然未归零,(因为当前栈内数据保持有序,则栈顶元素为最大值),则移除栈顶元素直至计数器归零
  6. 此时栈底到栈顶为有序递增,出栈后数据顺序必然颠倒,所以在返回之前将数组元素reverse
  7. 特殊判断:
    1. 若栈内为空,则返回string “0”
    2. 若数据头部有0 eg: ”001“ 当删除头部 0,再输出
class Solution {
public:
string removeKdigits(string num, int k) {stack<int>s;string s2;for (int i = 0; i < num.size(); i++){while(!s.empty() && num[i] <s.top()&&k>0){s.pop();k--;}s.push(num[i]);}//未删完从尾巴删while (k > 0){s.pop();k--;}//判空返回0if (s.empty()){s2.push_back('0');return s2;}while (!s.empty()){s2.push_back(s.top());s.pop();}//栈底含有0while (s2.back() == '0'&&s2.size()>1)s2.pop_back();reverse(s2.begin(), s2.end());return s2;
}
};

7. 「力扣」第581题:最短无序连续子数组

这道题的解题并没有用到栈,但是需要记忆该题的算法思想:

谁说一定要用单调栈解题?

  1. 寻找中间的子串使得整段数据有序
  2. 将原数据复制一份并排序,对比左右两侧最大的相等区间,中间部分即为所求
  3. 这道题虽然也出现在单调栈的题目集合中,但是使用单调栈的方法不一定简单易实现,需要我们灵活判断使用
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
//排序后,比对原数组,得到左边界与右边界vector<int>v = nums;sort(v.begin(), v.end());int i, j;for (i = 0, j = nums.size() - 1; i < j; ){if (nums[i] == v[i])i++;if (nums[j] == v[j])j--;else if (nums[i] != v[i] && nums[j] != v[j])break;}if (i>=j)return 0;return j-i+1;
}
};

总结:

  1. 不能判断的元素下标入栈,直至可以判断时出栈;
  2. 注意利用元素的下标区间的作用
  3. 是否需要首位哨兵结点,避免多余的判断

题目也许有简单算法、实现方法,也许我们用了另一种很艰难的算法实现了,但是也并不能说明这道题我们解的很好,要尝试多练习、多思考,不断寻找最优解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3030137.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

多维点分布的均匀性评估方法(NDD和Voronoi 图法)

评估多维点分布的均匀性是统计学和数据科学中的一个重要问题&#xff0c;特别是在模拟、空间分析和样本设计等领域。下面&#xff0c;我将详细介绍2种评估多维点分布均匀性的方法&#xff0c;包括它们的数学原理、实现公式以及各自的优缺点。 1. 最近邻距离法&#xff08;Neare…

复习了好久的软考中项,现在上半年不考了,该怎么办?

如果有更多学习时间的话&#xff0c;可以考虑报考高级职称&#xff0c;因为高级和中级职称的很多知识点有重叠&#xff0c;只需要再复习一下相关论文就可以了。 从2024年下半年开始&#xff0c;集成考试将采用最新版教材和大纲&#xff0c;与高级职称的新版教材内容相似度很高…

深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 &#x1f525; 引言&#x1f9f1; 原型基础⛓️ 原型链的形成&#x1f504; 修改原型的影响&#x1f3c1; 原型链的尽头为什么null标志着结束&#xff1f;实际意义 &#x1f310; &#x1f504; 继承的实现方式1. 原型链继承…

海外仓管理系统:为什么推荐基于云的SaaS模式,而不是本地部署

海外仓管理系统 是 海外仓 企业 使用 最多 的 软件 &#xff0c; 根据 公开 的 行业 数据 显示 &#xff0c; 几乎 8 4 % 的 海外仓 企业 都会 通过 海外仓 管理系统 来 管理 仓储 。 然而&#xff0c;市场上存在很多不同类型的海外仓管理系统可以选择&#xff0c;归结起来有两…

【Web】2023浙江大学生省赛初赛 secObj 题解

目录 step 0 step 1 step 2 step 3 题目本身是不难&#xff0c;简单复健一下 step 0 pom依赖就是spring 反序列化入口在./admin/user/readObj 输入流做了黑名单的过滤&#xff0c;TemplatesImpl不能直接打 可以jackson打SignedObject二次反序列化绕过 具体原理看下面这…

哪里有视频素材可以用?全视频素材都在哪里找?

在这个数字化快速发展的世界中&#xff0c;高清和4K视频素材对于提升视觉故事的品质至关重要。以下是一系列全球知名的视频素材网站&#xff0c;它们提供的高质量素材能够满足您从商业广告到个人项目的所有需求。 1. 蛙学府 以其庞大的创意资源库著称&#xff0c;订阅者可以无…

1.基于python的单细胞数据预处理-归一化

目录 归一化的引入移位对数皮尔森近似残差两个归一化方法的总结 参考&#xff1a; [1] https://github.com/Starlitnightly/single_cell_tutorial [2] https://github.com/theislab/single-cell-best-practices 归一化的引入 在质量控制中&#xff0c;已经从数据集删除了低质…

百面算法工程师 | 传统图像处理——OpenCV

本文给大家带来的百面算法工程师是传统图像处理的面试总结&#xff0c;文章内总结了常见的提问问题&#xff0c;旨在为广大学子模拟出更贴合实际的面试问答场景。在这篇文章中&#xff0c;我们将介绍一些集几何变换和图像平滑处理&#xff0c;并提供参考的回答及其理论基础&…

JAVA 双亲委派之一

JAVA 双亲委派之一 JVM类加载流程 java语言系统内置了众多类加载器&#xff0c;从一定程度上讲&#xff0c;只存在两种不同的类加载器&#xff1a;一种是启动类加载器&#xff0c;此类加载由C实现&#xff0c;是JVM的一部分&#xff1b;另一种就是所有其他的类加载器&#xf…

QT作业5

1、聊天室 服务器端 //头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QTcpSocket> #include <QList> #include <QListWidget> #include <QMessageBox> #include <QDebug> #includ…

第十五届蓝桥杯python B组省赛

前言&#xff1a; 这是我第一次参加蓝桥杯&#xff0c;成绩并不理想&#xff0c;我反思了一下午&#xff0c;我的问题主要是知识点学不透&#xff0c;题目做的太少&#xff0c;而且学习的时候少数时间不专心&#xff0c;但是&#xff0c;我能感觉到我的学习能力并不弱&#xf…

用云手机打造海外社媒矩阵

在全球经济一体化的大背景下&#xff0c;中国出海企业及B2B外贸公司正将海外社交媒体营销作为重要的市场拓展策略。为更好地触及不同受众群体&#xff0c;构建跨平台的社媒矩阵已成为企业营销的关键步骤。本文将探讨如何利用云手机技术&#xff0c;高效管理并运营多个海外社交媒…

CSS-页面导航栏实现-每文一言(过有意义的生活,做最好的自己)

&#x1f390;每文一言 过有意义的生活,做最好的自己 目录 &#x1f390;每文一言 &#x1f6d2;盒子模型 &#x1f453;外间距 (margin) &#x1f97c;边框 &#x1f45c;内边距 切换盒子模型计算方案&#xff1a; &#x1f3a2; 浮动布局 浮动特点 &#x1f3c6;导航…

Adobe Photoshop PS 25.6.0 解锁版 (最流行的图像设计软件)

前言 Adobe Photoshop 是一款专业强大的图片处理工具&#xff0c;从照片编辑和合成到数字绘画、动画和图形设计&#xff0c;一流的图像处理和图形设计应用程序是几乎每个创意项目的核心所在。利用 Photoshop 在桌面上的强大功能&#xff0c;您可以在灵感来袭时随时随地进行创作…

推荐3个实用的github开源项目

目录&#xff1a; 1、AI生成高清短视频 2、媒体平台爬虫 3、文本转语音项目

C++对象的拷贝构造函数

如果一个构造函数的第一个参数是类本身的引用,且没有其它参数(或者其它的参数都有默认值),则该构造函数为拷贝构造函数。 拷贝(复制)构造函数:利用同类对象构造一个新的对象 ●1.函数名和类同名 (构造函数) ●2.没有返回值 (构造函数) ●3.第一个参数必…

5.12母亲节营销攻略:TikTok助力出海品牌赢得用户心

母亲节&#xff0c;作为一个全球性的节日&#xff0c;不仅是表达对母亲的感激之情的时刻&#xff0c;也是品牌们展示创意、赢得用户心的黄金机会。2024母亲节将至&#xff0c;如何利用TikTok在母亲节这一特殊时刻进行营销&#xff0c;赢得用户的心&#xff0c;成为出海品牌必须…

Oracle count的优化-避免全表扫描

Oracle count的优化-避免全表扫描 select count(*) from t1; 这句话比较简单&#xff0c;但很有玄机&#xff01;对这句话运行的理解&#xff0c;反映了你对数据库的理解深度&#xff01; 建立实验的大表他t1 SQL> conn scott/tiger 已连接。 SQL> drop table t1 purge…

会话劫持攻击就在我们身边,我们要如何防范

会话劫持攻击&#xff08;Session Hijacking&#xff09;是一种网络攻击方式&#xff0c;攻击者通过某种手段获取到用户的会话标识&#xff08;Session ID&#xff09;&#xff0c;然后使用这个会话标识冒充合法用户进行恶意操作。这种攻击方式允许攻击者以合法用户的身份访问受…

【Linux】Linux——Centos7安装Nginx

不需要安装包 1.安装依赖 #查看 C 环境是否安装gcc -v #查看 zlib 是否安装cat /usr/lib64/pkgconfig/zlib.pc #查看 pcre 是否安装pcre-config --version 2.安装C #安装C yum install gcc-c 3.安装pcre yum install -y pcre pcre-devel 4.安装zlib #安装 yum install -y zlib…