力扣(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);  //用来表示该位置的括号是否可匹配:无法匹配的括号的位置全部置1
    stack<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);   //数组头部加入元素0
    heights.push_back(0);   //数组尾部加入元素0
    int 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题目分类 关注走一波

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Java Springboot宠物猫售卖管理系统

一、作品包含 源码数据库全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据库&#xff1a;…

Windows docker下载minio出现“Using default tag: latestError response from daemon”

Windows docker下载minio出现 Using default tag: latest Error response from daemon: Get "https://registry-1.docker.io/v2/": context deadline exceeded 此类情况&#xff0c;一般为镜像地址问题。 {"registry-mirrors": ["https://docker.re…

数据结构查找-哈希表(开发地址法+线性探测法)+(创建+查找+删除代码)+(C语言代码)

#include<stdlib.h> #include<stdio.h> #include<stdbool.h> #define NULLKEY -1//单元为空 #define DELKEY -2//单元内容被删除 #define M 20 typedef struct {int key;//关键字int count;//统计哈希冲突探测次数 }HashTable; //插入到哈希表 void InsertHT…

视频直播5G CPE解决方案:ZX7981PG/ZX7981PMWIFI6网络覆盖

方案背景 视频直播蓬勃发展的当下&#xff0c;传统直播网络联网方式的局限性越来越明显。目前传统直播的局限性主要集中在以下几个方面&#xff1a; 传统直播间网络架构条件有限&#xff0c;可连接WIFI数量少&#xff0c;多终端同时直播难以维持&#xff1b;目前4G网络带宽有限…

【电子设计】按键LED控制与FreeRTOS

1. 安装Keilv5 打开野火资料,寻找软件包 解压后得到的信息 百度网盘 请输入提取码 提取码:gfpp 安装526或者533版本都可以 下载需要的 F1、F4、F7、H7 名字的 DFP pack 芯片包 安装完 keil 后直接双击安装 注册操作,解压注册文件夹后根据里面的图示步骤操作 打开说明 STM…

vue3【实战】切换白天黑夜(暗黑模式)【组件封装】DarkMode.vue

效果预览 原理解析 切换为暗黑模式时&#xff0c;会在 html 标签上添加样式类 dark导入 ElementPlus 的暗黑模式样式后&#xff0c; ElementPlus 组件会自动响应暗黑模式自定义组件需用 UnoCSS 的 dark: 语法自定义暗黑模式的样式 代码实现 技术方案 vue3 vite ElementPlus …

基于单片机的多功能环保宠物窝设计

本设计基于单片机设计的多功能环保宠物窝&#xff0c;利用温湿度传感器、压力传感模块、气味传感模块、红外测温传感器、通信模块、显示模块、清扫部件等&#xff0c;使其能够实现自动检测并调节温湿度、补充宠物食物、检测宠物体温健康并出现异常时进行报警、自动清扫消毒宠物…

MySql结合element-plus pagination的分页查询

实现效果如下&#xff1a; 重点&#xff1a;使用mysql查询的limit和offset 原生SQL写法&#xff1a; select c.id as deptid,c.name as department,position,a.name staffname,2024-11 as shijian ,CASE WHEN b.shijian IS NULL THEN no ELSE yes END AS submit from fa_wecom…

vue使用List.reduce实现统计

需要对集合的某些元素的值进行计算时&#xff0c;可以在计算属性中使用forEach方法 1.语法&#xff1a;集合.reduce ( ( 定义阶段性累加后的结果 , 定义遍历的每一项 ) > 定义每一项求和逻辑执行后的返回结果 , 定义起始值 ) 2、简单使用场景&#xff1a;例如下面…

Spring Boot汽车资讯:科技与速度的交响

3系统分析 3.1可行性分析 通过对本汽车资讯网站实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本汽车资讯网站采用SSM框架&#xff0c;JAVA作为开发语言&#…

前端页面自适应等比例缩放 Flexible+rem方案

在移动互联网时代&#xff0c;随着智能手机和平板电脑的普及&#xff0c;前端开发者面临的一个重要挑战是如何让网页在不同尺寸和分辨率的设备上都能良好地显示。为了应对这一挑战&#xff0c;阿里巴巴的前端团队开发了 flexible.js&#xff0c;旨在提供一种简单有效的解决方案…

记录一下在原有的接口中增加文件上传☞@RequestPart

首先&#xff0c;咱声明一下&#xff1a; RequestBody和 MultipartFile 不可以 同时使用&#xff01;&#xff01;&#xff01; 因为这两者预期的请求内容类型不同。RequestBody 预期请求的 Content-Type 是 application/json 或 application/xml&#xff0c;而 MultipartFile …

HTML5实现剪刀石头布小游戏(附源码)

文章目录 1.设计来源1.1 主界面1.2 皮肤风格1.2 游戏中界面 2.效果和源码源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/143798520 HTM…

[Qt platform plugin问题] Could not load the Qt platform plugin “xcb“

Qt platform plugin 是 Qt 应用程序启动时加载的插件。不同的平台有不同的插件。 常见的插件有:linuxfb Wayland xcb 简单来说就是启动一个GUI程序, 离不开这些插件.选择其中一个就好 出现这个问题要么就是没有插件&#xff0c;要么就是插件依赖的库没有。 要么就是插件选则的…

【qt】控件2

1.frameGeometry和Geometry区别 frameGeometry是开始从红圈开始算&#xff0c;Geometry从黑圈算 程序证明&#xff1a;使用一个按键&#xff0c;当按键按下,qdebug打印各自左上角的坐标&#xff08;相当于屏幕左上角&#xff09;&#xff0c;以及窗口大小 Widget::Widget(QWid…

Idea中创建和联系MySQL等数据库

备注&#xff1a;电脑中要已下好自己需要的MySQL数据库软件 MySQL社区版下载链接&#xff1a; https://dev.mysql.com/downloads/installer/ 优点&#xff1a; 1.相比与在命令行中管理数据库&#xff0c;idea提供了图形化管理&#xff0c;简单明了&#xff1b; 2.便于与后端…

【Unity】网格系统:物体使用网格坐标定位

需求分析 前面物体放置在地板上都是地板任意位置放置&#xff0c;本节开始对物体放置的位置做限制。 建立网格&#xff0c;网格可以设置起始世界坐标、单元格大小和规格&#xff1b;单元格中包括内部物体的信息&#xff1b;物体的位置通过网格的坐标确定&#xff1b;单元格中…

网络协议(4)拥塞控制

之前已经说过了tcp也是会考虑网络的情况的&#xff0c;也就是当网络出现问题的时候tcp不会再对报文进行重传。当所有的用户在网络不好的时候都不会对丢失的报文进行重传。这样就会防止网络瘫痪。 这样的机制也就是tcp会进行拥塞控制。 拥塞控制 所谓的慢启动看下面这张图就能…

集群聊天服务器(8)用户登录业务

目录 登录状态业务层代码数据模型层代码记录用户的连接信息以及线程安全问题客户端异常退出业务 登录状态 登录且状态变为online 业务层代码 #include "chatservice.hpp" #include "public.hpp" #include <string> #include <muduo/base/Loggi…

生成自签名证书并配置 HTTPS 使用自签名证书

生成自签名证书 1. 运行 OpenSSL 命令生成证书和私钥 在终端中输入以下命令&#xff0c;生成自签名证书和私钥文件&#xff1a; sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout self_signed.key -out self_signed.pem-x509&#xff1a;生成自签名证书。…