力扣(leetcode)题目总结——辅助栈篇
leetcode 经典题分类
- 链表
- 数组
- 字符串
- 哈希表
- 二分法
- 双指针
- 滑动窗口
- 递归/回溯
- 动态规划
- 二叉树
- 辅助栈
本系列专栏:点击进入 leetcode题目分类 关注走一波
前言:本系列文章初衷是为了按类别整理
出力扣(leetcode)最经典题目,一共100多道题,每道题都给出了非常详细的解题思路、算法步骤、代码实现。很多同学刚开始刷题都是按照力扣顺序刷题,其实这样对新手不太适用,刷题效果也很不好。因为力扣题目顺序是随机的,并没有按照算法分类,导致同一类型的算法强化训练不够,最后刷完也是迷迷糊糊的。所以本系列文章就是来帮你完成算法分类,针对每种算法做强化训练,保证让你以后遇到题目直接秒杀!
文章目录
- leetcode 经典题分类
- 有效的括号
- 最长有效括号
- 接雨水
- 柱状图中最大的矩形
有效的括号
【题目描述】
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串s,判断字符串是否有效。
有效字符串需满足:
-
左括号必须用相同类型的右括号闭合;
-
左括号必须以正确的顺序闭合;
-
每个右括号都有一个对应的相同类型的左括号。
【输入输出实例】
示例 1:
输入:s = “()”
输出:true
示例 2:
输入:s = “()[]{}”
输出:true
示例 3:
输入:s = “(]”
输出:false
【算法思路】
先来分析一下,这里有三种不匹配的情况:
-
字符串里左方向的括号多余了,所以不匹配;
-
括号没有多余,但是括号的类型没有匹配上;
-
字符串里右方向的括号多余了,所以不匹配;
针对三种不匹配情况的代码:
-
第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false;
-
第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符,所以return false;
-
第三种情况:遍历字符串匹配的过程中,栈已为空,没有匹配的括号,说明右括号没有找到对应的左括号,所以return false。
匹配成功的情况:
字符串遍历完之后,栈是空的,就说明全都匹配了。
技巧:
在遇到左括号时,不要让左括号本身入栈,而让相应的右括号入栈。则在遇到右括号匹配时,就只需要比较当前右括号和栈顶元素相不相等就可以了,比左括号入栈代码实现更简单。
【算法描述】
//括号匹配是使用辅助栈解决
bool isValid(string s)
{stack<char> sta; //辅助栈for(char ch : s) //遍历给定的括号字符串{switch(ch){//技巧:遇到左括号就入栈其对应的右括号case '(':sta.push(')');break;case '[':sta.push(']');break;case '{':sta.push('}');break;default: //遇到右括号就检查是否匹配,检查本身与栈顶元素是否一样if(sta.empty() || sta.top() != ch) //栈为空 或 本身与栈顶元素不一样,都说明括号不匹配{return false;}sta.pop(); //若一样,则出栈}}return sta.empty(); //最后若有未出栈的元素说明,有不匹配的项
}
最长有效括号
【题目描述】
给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
【输入输出实例】
示例 1:
输入:s = “(()”
输出:2
示例 2:
输入:s = “)()())”
输出:4
示例 3:
输入:s = “()(()”
输出:2
示例 4:
输入:s = “”
输出:0
【算法思路】
创建一个大小为len的容器来表示各个括号的是否可匹配。用栈模拟一遍括号匹配,将所有无法匹配的括号的位置全部置1。例如:"()(()“对应为[0, 0, 1, 0, 0];再例如:”)()((())"的对应为[1, 0, 0, 1, 0, 0, 0, 0]。
经过这样的处理后,此题就变成了寻找最长的连续0的长度
【算法描述】
int longestValidParentheses(string s)
{int len = s.size();vector<int> v(len); //用来表示该位置的括号是否可匹配:无法匹配的括号的位置全部置1stack<int> stk; //栈内存放括号下标int maxLong = 0; //记录括号匹配的最大长度for(int i = 0; i < len; i++){if(s[i] == '(') //遇到左括号就入栈{stk.push(i);}else //遇到右括号就判断{if(stk.empty()) //多余的右括号是不需要的,标记1{v[i] = 1;}else //匹配成功{stk.pop();}}}while(!stk.empty()) //未匹配的左括号是不需要的,标记1{v[stk.top()] = 1;stk.pop();}// 寻找标记与标记之间的最大长度,即找最长的连续的0的长度int num = 0;for(int i = 0; i < len; i++){if(v[i] == 0){num++;}else{maxLong = max(maxLong, num);num = 0;}}maxLong = max(maxLong, num); return maxLong;
}
接雨水
【题目描述】
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
【输入输出实例】
示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
示例 2:
输入:height = [4,2,0,3,2,5]
输出:9
【算法思路】
首先,要知道单调栈是按照行方向来计算雨水,如图:
单调栈内元素的顺序:从栈头到栈底的顺序应该是从小到大的顺序。因为一旦发现要添加的柱子高度大于栈顶元素,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。如图:
因为长就是通过柱子的高度来计算,宽是通过柱子之间的下标来计算,通过长*宽来计算雨水面积,所以栈中应该存放下标,计算的时候用下标对应的柱子高度即可。
算法过程就是:用单调栈不断入栈每个柱子对应的下标,当要入栈的柱子高度大于栈顶元素时,要先将元素出栈保留,即为凹槽的底部bottom。此时的栈顶元素为凹槽左边的柱子left,要入栈的柱子为凹槽右边的柱子right。则可算出这个凹槽中盛放水的高度:
rainHeight = min(height[left], height[right]) - height[temp];
宽度为:rainWidth = right - left - 1;
则可算出该凹槽中盛放水的体积,根据上述操作遍历(不断入栈出栈)完每根柱子,将得到的雨水量相加起来即可。
【算法描述】
//双指针法(超时)
int trap(vector<int>& height)
{int len = height.size(); int sum = 0; //记录接的雨水for(int i = 1; i < len - 1; i++) //遍历每列(除第一列和最后一列,因为它们不会接雨水){int maxLeft = 0; //左边最高柱子的高度int maxRight = 0; //右边最高柱子的高度for(int left = i - 1; left >= 0; left--) //找左边最高柱子{maxLeft = max(maxLeft, height[left]);}for(int right = i + 1; right < len; right++) //找右边最高柱子{maxRight = max(maxRight, height[right]);if(maxRight > maxLeft) //因为最后要取两者最小,所以只要超过左边最高柱子,直接退出{break;}}int rainHeight = min(maxLeft, maxRight) - height[i]; //计算雨水高度sum += (rainHeight > 0) ? rainHeight : 0;}return sum;
}//动态规划——相当于双指针法,只是用动态规划来提前求出每个柱子的最大左边柱子和最大右边柱子
int trap(vector<int>& height)
{int len = height.size(); int sum = 0; //记录接的雨水vector<int> maxLeft(len); //记录第i列左边柱子最大高度vector<int> maxRight(len); //记录第i列右边柱子最大高度maxLeft[0] = height[0]; //初始化maxRight[len-1] = height[len-1];//记录每个柱子左边柱子最大高度for(int i = 1; i < len-1; i++){maxLeft[i] = max(maxLeft[i-1], height[i]);}//记录每个柱子右边柱子最大高度for(int i = len-2; i > 0; i--){maxRight[i] = max(maxRight[i+1], height[i]);}//遍历每列算雨水量再求和for(int i = 1; i < len - 1; i++){int rainHeight = min(maxLeft[i-1], maxRight[i+1]) - height[i];sum += (rainHeight > 0) ? rainHeight : 0;}return sum;
}//单调栈
int trap(vector<int>& height)
{int sum = 0; //记录接的雨水stack<int> s; //单调栈s.push(0);for(int i = 1; i < height.size(); i++){//如果栈不空并且当前指向的高度大于栈顶高度就一直循环while(!s.empty() && height[i] > height[s.top()]){int temp = height[s.top()]; //取出要出栈的元素s.pop(); //出栈if(!s.empty()) //出栈一个后如果栈不为空,就开始算雨水{int rainHeight = min(height[i], height[s.top()]) - temp; //计算雨水高度:-temp相当于是减去i和s.top()两列构成容器的底部柱子的高度int rainWidth = i - s.top() - 1; //雨水宽度sum += rainHeight * rainWidth; //雨水量}}s.push(i); //每根柱子要入栈}return sum;
}
柱状图中最大的矩形
【题目描述】
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为1。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
【输入输出实例】
示例 1:
输入:heights = [2,1,5,6,2,3]
输出:10
示例 2:
输入:heights = [2,4]
输出:4
【算法思路】
本题目与42、接雨水非常相似,也是有三种方法,如下:
(1)双指针法和动态规划
接雨水是找每列中左侧最高的柱子和右侧最高的柱子。找到后该列的雨水高度可得到,宽度为1,就可得到该列的雨水量,再把每列的雨水累加起来即可;
本题是找每个柱子左右两侧最后一个大于等于该柱子高度的柱子。找到后勾勒出来的矩形的宽度为左右两侧这两列的下标差,高度就是本列柱子的高度,则可计算出本列勾勒出来最大矩形的面积,再把每列勾勒出来的矩形面积求出来,再找最大。
(2)单调栈
接雨水是找每个柱子左右两边第一个大于该柱子高度的柱子,而本题是找每个柱子左右两边第一个小于该柱子的柱子。所以从栈头到栈底的顺序应该是从大到小的顺序。
如上图,可知只有栈里从大到小的顺序,才能保证栈顶元素找到左右两边第一个小于栈顶元素的柱子。
其实就是栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度。其中栈顶元素为矩形高度,栈顶的下一个元素以及要入栈的元素的下标构成了矩阵的宽度。
其实可以把这个过程想象成锯木板,如果木板都是递增的那我很开心不用管。如果突然遇到一块木板 i 矮了一截,那我就先找之前最戳出来的一块(其实就是第 i-1 块),计算一下这个木板单独的面积,然后把它锯成次高的,这是因为我之后的计算都再也用不着这块木板本身的高度了。再然后如果发现次高的仍然比现在这个 i 木板高,那我继续单独计算这个次高木板的面积(应该是第 i-1 和 i-2 块组成的木板),再把它俩锯短。直到发现不需要锯就比第 i 块矮了,那我继续开开心心往右找更高的。当然为了避免到了最后一直都是递增的,所以可以在最后加一块高度为0的木板。
这个算法的关键点是把那些戳出来的木板早点单独拎出来计算,然后就用不着这个值了。
【算法描述】
//双指针法——超时
int largestRectangleArea(vector<int>& heights) {int n = heights.size();int maxArea = 0; //记录矩形的最大面积for(int i = 0; i < n; i++) //遍历每根柱子{int left = i - 1;int right = i + 1;//找左边最后一个大于等于heights[i]的下标for(; left >= 0; left--){if(heights[left] < heights[i]){break;}}//找右边最后一个大于等于heights[i]的下标for(; right < n; right++){if(heights[right] < heights[i]){break;}}int height = heights[i]; //矩形高度int width = right - left - 1; //矩形宽度maxArea = max(maxArea, height*width); //计算最大面积}return maxArea;
}//动态规划
int largestRectangleArea(vector<int>& heights)
{int n = heights.size();int maxArea = 0; //记录矩形的最大面积vector<int> left(n);vector<int> right(n);left[0] = -1; //初始化right[n-1] = n;//记录每个柱子左边第一个高度小于该柱子的下标for(int i = 1; i < n; i++){int index = i - 1;while(index >= 0 && heights[i] <= heights[index]){index = left[index]; //动态规划更新,上一次已经求过,不用重复求}left[i] = index;}//记录每个柱子右边第一个高度小于该柱子的下标for(int i = n - 2; i >= 0; i--){int index = i + 1;while(index < n && heights[i] <= heights[index]){index = right[index]; //动态规划更新,上一次已经求过,不用重复求}right[i] = index;}//计算矩形的最大面积for(int i = 0; i < n; i++){int height = heights[i]; //矩形高度int width = right[i] - left[i] - 1; //矩形宽度maxArea = max(maxArea, height*width); //计算最大面积}return maxArea;
}//单调栈
int largestRectangleArea(vector<int>& heights) {heights.insert(heights.begin(), 0); //数组头部加入元素0heights.push_back(0); //数组尾部加入元素0int maxArea = 0; //记录矩形的最大面积stack<int> s; //单调栈(递减)s.push(0);for(int i = 1; i < heights.size(); i++) //遍历各柱子{while(heights[i] < heights[s.top()]){int height = heights[s.top()]; //矩形高度//int width = i - s.top(); //这时得到的矩形宽度没有把之前出栈的柱子宽度算上,因为之前出栈的柱子高度肯定大于它后面的柱子高度,所以才被出栈,那么要把前面已出栈的柱子宽度也要算上s.pop();int width = i - s.top() - 1; //矩形宽度:把前面已出栈的宽度也要算上 maxArea = max(maxArea, height*width); //计算最大面积}s.push(i);}return maxArea;
}
恭喜你全部读完啦!古人云:温故而知新。赶紧收藏关注起来,用之前再翻一翻吧~
📣推荐阅读
C/C++后端开发面试总结:点击进入 后端开发面经 关注走一波
C++重点知识:点击进入 C++重点知识 关注走一波
力扣(leetcode)题目分类:点击进入 leetcode题目分类 关注走一波
相关文章:

力扣(leetcode)题目总结——辅助栈篇
leetcode 经典题分类 链表数组字符串哈希表二分法双指针滑动窗口递归/回溯动态规划二叉树辅助栈 本系列专栏:点击进入 leetcode题目分类 关注走一波 前言:本系列文章初衷是为了按类别整理出力扣(leetcode)最经典题目,…...

如何处理 iOS 客户端内 Webview H5 中后台播放的音视频问题
目录 问题描述Page Visibility API 的应用什么是 Page Visibility API?使用 Page Visibility API 暂停音视频完整解决方案1. 监听媒体的播放和暂停事件2. 防止自动播放3. 结合 Intersection Observer 进行媒体控制4. 手动处理应用生命周期中的事件 问题描述 在 iOS…...

C++的一些模版
1、不限制次数的输入数据 vector<int> nums;int num;while (cin >> num) {nums.push_back(num);if (cin.get() \n) break;}2、取模模版 template<int kcz> struct ModInt { #define T (*this)int x;ModInt() : x(0) {}ModInt(int y) : x(y > 0 ? y : y…...

spring boot整合https协议
整体目录 1. 生成SSL证书 首先,使用keytool生成一个自签名证书。打开命令行工具并运行以下命令: keytool -genkeypair -alias myserver -keyalg RSA -keysize 2048 -keystore keystore.jks -validity 365 这将创建一个名为keystore.jks的文件…...

服务器开机即占用大量内存,解决
1.服务器开机两分钟不到,内存使用飙升 [rootlocalhost ~]# top #查看是否有了明显的内存占用程序 2.上述未果,查看是否有违规的开机自启项 [rootlocalhost ~]# chkconfig --list 3.上述无果,查看开启启动加载项内容 上网搜后ÿ…...

Keil uvision的edition
0 Preface/Foreword 0.1 参考网址 https://zhuanlan.zhihu.com/p/456069876 1 Keil版本介绍 版本介绍: Keil Lite(免费版):最多32KB代码,无法使用中间件Keil Essential(基础版):没…...

[每周一更]-(第123期):模拟面试|消息队列面试思路解析
文章目录 22|消息队列:消息队列可以用来解决什么问题?1. 你用过消息队列吗?主要用来解决什么问题?异步、削峰和解耦你能各举一个例子吗?2. 你用的是哪个消息队列?为什么使用它而不用别的消息队列?3. 为什么你一定要用消息队列?不用行不行?不用有什么缺点?4. 在对接多…...

游戏引擎学习第12天
视频参考:https://www.bilibili.com/video/BV1yom9YnEWY 这节没讲什么东西,主要是改了一下音频的代码 后面有介绍一些alloc 和malloc,VirtualAlloc 的东西 _alloca 函数(或 alloca)分配的是栈内存,它的特点是: 生命周…...

深入理解Flutter生命周期函数之StatefulWidget(一)
目录 前言 1.为什么需要生命周期函数 2.开发过程中常用的生命周期函数 1.initState() 2.didChangeDependencies() 3.build() 4.didUpdateWidget() 5.setState() 6.deactivate() 7.dispose() 3.Flutter生命周期总结 1.调用顺序 2.函数调用时机以及主要作用 4.生…...

413: Quick Sort
解法: #include <bits/stdc.h> using namespace std; const int N1e55; int a[N]; int n;int main(int argc, char** argv) {cin>>n;for (int i0;i<n;i) cin>>a[i];sort(a,an);for (int i0;i<n;i) cout<<a[i]<<" "…...

vue之axios根据某个接口创建实例,并设置headers和超时时间,捕捉异常
import axiosNew from axios;//给axios起个别名//创建常量实例 const instanceNew axiosNew.create({//axios中请求配置有baseURL选项,表示请求URL的公共部分,url baseUrl requestUrlbaseURL: baseURL,//设置超时时间为20秒timeout: 20000,headers: {…...

Pandas数据透视表:交叉分析与聚合计算
大家好,在数据分析中,数据透视表(Pivot Table)是一种强大的工具,用于交叉分析和聚合计算。Pandas库中的数据透视表功能,使我们能够在多维数据中快速生成汇总表、统计特定维度的聚合数据,帮助揭示…...

软件设计师考试大纲
文章目录 一 、考 试 说 明1. 考试目标2. 考试要求3. 考试科目设置 二、考 试 范 围考试科目1:计算机与软件工程知识1. 计算机系统基础知识1.1计算机内数据的表示及运算1.2 其他数学基础知识1.3 计算机硬件基础知识1.3.1 计算机系统的组成、体系结构分类及特性1.3.2 存储系统1.…...

一文说清C++类型转换操作符(cast operator)
一 前言 大家在编程时,一定会遇到要做类型转换的应用场景。 但是,C风格的类型转换太强大,太危险,它允许将一个给定类型转换成我们想要的任何其他类型。 所以在C中,提供了一些更安全和更明确的类型转换操作符ÿ…...

MOSFET电路栅源极GS之间并联电容后,MOS炸管原因分析
1、前言 在介绍,在进行MOSFET相关的电路设计时,可能会遇到MOSFET误导通的问题,为了解决此问题,我们提出了两种方法,一种是增大MOSFET栅极串联电阻的阻值,另外一种是在MOSFET栅-源极之间并联一个电容&#…...

gitHub常用操作
gitHub常用操作 1、把项目拉下来2、添加上游仓库3、进入分支4、从上游仓库拉取更新 1、把项目拉下来 在对应项目的右上角点击fork,fork下来:将远程仓库复制到个人仓库 在创建好的分支文件夹下使用 git clone自己远程仓库下的http地址(fork…...

[项目代码] YOLOv5 铁路工人安全帽安全背心识别 [目标检测]
YOLOv5是一种单阶段(one-stage)检测算法,它将目标检测问题转化为一个回归问题,能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法(如Faster R-CNN),YOLOv5具有更高的…...

Java 垃圾回收机制(GC)概览
简介 Java垃圾收集、堆和运行时编译器默认选择 jdk1.9开始,默认使用G1收集器,GC Threads的最大数量受堆大小和可用CPU资源限制初始堆大小为物理内存的1/64最大堆大小为物理内存的1/4分层编译器,同时使用C1和C2 JVM 垃圾收集器可以为配置优…...

Kafka节点服役和退役
1 服役新节点 1)新节点准备 (1)关闭 bigdata03,进行一个快照,并右键执行克隆操作。 (2)开启 bigdata04,并修改 IP 地址。 vi /etc/sysconfig/network-scripts/ifcfg-ens33修改完…...

Git如何简单使用
文章目录 GitGitlabGitLab和GitHub有什么区别?Gitlab简单使用Gitlab常用指令Git Git是一个分布式版本控制系统。 它用于记录文件的修改历史,方便多人协作开发软件等项目。例如一个软件开发团队,成员们会频繁修改代码,Git可以追踪每个人的修改内容、时间等信息。 主要功能…...

酒水分销积分商城小程序开发方案php+uniapp
酒水分销积分商城小程序开发,开发语言后端php,前端uniapp。核心功能模块:酒水商城、积分商城、二级分销、抽奖、优惠券。可以二开或定制。协助部署搭建。...

MTU-内核态(数据链路层或网络接口上能够传输的最大数据包大小)
MTU(最大传输单元,Maximum Transmission Unit)是网络中用于表示数据链路层或网络接口上能够传输的最大数据包大小。 1. 工作原理 MTU 决定了一个数据包(包括头部和数据部分)的最大长度。它影响到数据的传输ÿ…...

React的基础API介绍(一)
目录 useEffect1. 替代生命周期方法2. 副作用管理3. 依赖项数组4. 多次使用5. 与闭包配合6. 支持异步操作7. 减少样板代码 注意事项useEffetct是如何拿到变量count最新的值?1. 每次渲染都会创建新的函数作用域2. 闭包捕获最新的状态值3. useEffect 的执行时机 useLa…...

【Electron】总结:如何创建Electron+Element Plus的项目
我将结合官网手册与AI问到的信息,直接给出步骤,与命令。 一、准备环境 首先在C盘Users,你的登录的账号名文件夹下,编辑.npmrc文件。添加镜像地址。 如果使用了yarn,则是.yarnrc。可以全部都配置。 npm install -g …...

从依托指标字典到 NoETL 自动化指标平台,指标口径一致性管理的进阶
今天,我们一起来梳理和盘点下不同代际指标平台如何实现指标口径一致性管理: 第一代:指标口径登记与管理 第一代指标平台聚焦于指标口径的登记与管理,依托指标字典实现企业指标口径的有效检索与管理功能。 此阶段,业…...

嵌入式面试题练习 - 2024/11/15
欢迎找我进行职业规划,超值的自我投资 -> 嵌入式软件工程师一对一指导 1.设有定义char *p[]{"Shanghai","Beijing","Honkong"};则结果为j字符的表达式是() A *p[1] 3 B *(p[1] 3) C *(p[3] 1) D p[3] […...

分析http话术异常挂断原因
用户反馈在与机器人通话时,自己明明有说话,但是通话还是被挂断了,想知道原因。 分析日志 我们根据用户提供的freeswitch日志分析:发现是因为超时导致话术执行hangup动作,结束了通话。 从这一行向上分析日志ÿ…...

云岚到家 秒杀抢购
目录 秒杀抢购业务特点 常用技术方案 抢券 抢券界面 进行抢券 我的优惠券列表 活动查询 系统设计 活动查询分析 活动查询界面显示了哪些数据? 面向高并发如何提高活动查询性能? 如何保证缓存一致性? 数据流 Redis数据结构设计 如…...

【WPF】Prism库学习(一)
Prism介绍 1. Prism框架概述: Prism是一个用于构建松耦合、可维护和可测试的XAML应用程序的框架。它支持WPF、.NET MAUI、Uno Platform和Xamarin Forms等多个平台。对于每个平台,Prism都有单独的发布版本,并且它们在不同的时间线上独立开发。…...

0 -vscode搭建python环境教程参考(windows)
引用一篇非常详细的vscode搭建python环境教程 链接:vscode安装以及配置Python基本环境 以下是VSCode和PyCharm的对比 个人更建议使用VSCode Visual Studio Code (VSCode) Visual Studio Code 是由微软开发的一款免费、开源的轻量级代码编辑器。它支持多种编程语…...