【ONE·基础算法 || 栈 】

news2025/1/12 20:45:07

在这里插入图片描述

总言

  主要内容:编程题举例,熟悉理解以栈此类数据结构为主的题型。
  
  

文章目录

  • 总言
  • 1、栈
  • 2、删除字符中的所有相邻重复项(easy)
    • 2.1、题解
  • 3、比较含退格的字符串(easy)
    • 3.1、题解
  • 4、基本计算器 II(medium)
    • 4.1、题解
  • 5、字符串解码(medium)
    • 5.1、题解
  • 6、验证栈序列(medium)
    • 6.1、题解
  • Fin、共勉。

  
  
  
  

1、栈

  栈(Stack)是一种重要的数据结构,具有后进先出(LIFO,Last In First Out)的特性。在算法设计和实现中,栈的应用非常广泛,此类题常见以模拟为主,但关键在于能否想到使用栈来完成。
  
  1、①可以直接使用C++标准库提供了std::stack模板类(实现了栈的功能)。②也可以使用数组或其它结构直接模拟栈的思想特性。
  2、除了下述习题,这里附带其它有关栈的习题链接:
    收录一(有效的括号、用队列实现栈、用栈实现队列、设计循环队列);
    收录二(最小栈、栈的压入与弹出序列、逆波兰表达式求值)
  
  
  

2、删除字符中的所有相邻重复项(easy)

  题源:链接。

在这里插入图片描述
  
  

2.1、题解

  1)、思路分析
  说明:根据题意,要判断当前元素是否为重复项,则需要知道其前一个元素的信息,可用「栈」来保存信息,直接取栈顶元素与当前遍历元素判断即可。
  这里有一个优化细节,若直接使用「栈」std::stack来模拟整个过程,结束后还需要从栈中将最终获取到的结果取出。因此,不如直接用「数组」模拟栈特性结构:这里以数组尾部为栈顶,「尾插尾删」即「进栈出栈」。如此一来,数组中存留的内容就是最后的需要返回的结果。
在这里插入图片描述

  
  
  2)、题解
  注意细节:string::back()的使用:

If the string is not empty, the function never throws exceptions (no-throw guarantee).
Otherwise, it causes undefined behavior.
class Solution {
public:
    string removeDuplicates(string s) {
        string ret(s,0,1);//用数组模拟栈,栈堆即数组尾部。
        for(int i = 1; i < s.size(); ++i)//遍历,入栈
        {
            if(!ret.empty() && s[i] == ret.back()) //栈不为空,且栈顶元素与当前待入栈元素相同
                ret.pop_back();// 出栈
            else
                ret.push_back(s[i]);// ⼊栈
        }
        return ret;
    }
};

  简化版写法:实则为各接口的调用。

class Solution {
public:
    string removeDuplicates(string s) {
        string ret; // 搞⼀个数组,模拟栈结构即可
        for (auto ch : s) {
            if (ret.size() && ch == ret.back())//栈中有元素并且栈顶元素和当前遍历到的元素相同
                ret.pop_back(); // 出栈
            else
                ret += ch; // ⼊栈
        }
        return ret;
    }
};

  
  
  
  
  
  
  

3、比较含退格的字符串(easy)

  题源:链接。

在这里插入图片描述

  
  

3.1、题解

  1)、思路分析
  需要理解这里退格的含义:实则为退格键(Backspace)的功能操作,使光标左移一格。
  举例:对于给定字符串ab#,当前文本框中输入为ab,遇到#退格,则文本框汇中输入为a
  
  整体思路: 此题可划分为模拟。①先根据给定字符串获得模拟后的文本结果,②再比较两字符串是否相同。
  
  对①模拟过程,由于这里退格符合「后进先出」的特性,因此可以使用「栈」结构来模拟退格的过程。为了方便统计结果,可使用「数组」(这里是字符串string)来模拟实现栈结构

当遇到⾮ # 字符时,直接进栈;
当遇到 # 时,栈顶元素出栈。

  此外,需要注意题目提示:对空文本输入退格字符,文本继续为空。 在模拟中,则表示栈中元素为空时,不能再继续出栈。
  
  
  2)、题解

class Solution {
public:
    string newString(string& s)
    {
        string ret;//模拟栈结构,数组尾为栈顶
        for(auto ch : s)
        {
            if(ch == '#') 
            {
                if(!ret.empty()) //防止连续多个####导致文本为空,此时不能再删除(空文本输入退格字符,文本继续为空:"y#f#o##f")
                    ret.pop_back();
            }
            else ret += ch;
        }
        return ret;
    }
    bool backspaceCompare(string s, string t) {
        //获取输出结果
        string news = newString(s);
        string newt = newString(t);
        //比较
        return news.compare(newt) == 0;//也可以直接比较:return newString(s) == newString(t);
    }
};

  
  
  
  
  
  
  

4、基本计算器 II(medium)

  题源:链接。

在这里插入图片描述

  
  

4.1、题解

  1)、思路分析
  注意这里提示内容,这里的基本计算器减少了实际需要处理的工作量。根据题目可以得知以下细节内容:
  1、运算符优先级:给定字符串中只有「加减乘除」四个运算,没有括号等改变优先级。可以知道的是乘除运算在加减之前。
  2、字符串中有空格,非有效字符,需要对其处理。
  3、大数提取:由于是字符串,这里给定的运算数不一定只有个位,如XXX ▢ XX ,因此这里涉及对多位数的字符提取(相关操作之前遇到过:高精度求和)。
  
  此题题解: 利用栈模拟计算过程vector<int> ,先计算乘除法,其结果用栈保存,最后对栈中剩余元素进行求和(加减法),即最终结果。
根据遇到的运算符,分情况讨论即可。

  使用一个变量op来记录遇到的操作符,在遍历时:
  1、若遇到操作符op:更新当前操作符。
  2、若遇到数字,①先将完整的运算数num1提取出来,②根据最新一次保存的操作符op,判断后续操作。
    a、若op == '+',将运算数num1入栈;
    b、若op == '-',将运算数的相反数-num1入栈。
    c、若op == '*',提取栈顶元素num2,与num1进行乘运算,再将运算结果放回栈中。(这里简化操作:可以直接将num1乘到栈顶元素上(使用vector作为栈,back()返回引用值)。)
    d、若op == '/',提取栈顶元素num2,与num1进行除运算,再将运算结果放回栈中。(这里简化操作:可以直接将num1除到栈顶元素上(使用vector作为栈,back()返回引用值)。)

在这里插入图片描述
  
  
  2)、题解

class Solution {
public:
    int calculate(string s) {
        
        char op = '+';//保存最新一次操作符
        vector<int> tmp;//用于模拟栈(保存有历史运算数)
        int i = 0; int len = s.size();
        while(i < len)
        {
            //当前i是否为空格
            if(s[i] == ' ') i++;

            //判断当前位置是操作符还是运算数
            if(!isdigit(s[i]) && s[i] != ' ')
            {   //当前遍历到的是操作符
                op = s[i++];//更新op,遍历下一个位置
            }
            else
            {   //当前遍历到的是操作数
                //1、提取操作数
                int num1 = 0;//当前遍历提取到的运算数
                while(i < len && isdigit(s[i]))
                {
                    num1 = num1*10 + (s[i]-'0');
                    i++;
                }

                //2、根据该数的左侧操作符(op中保存的值),判断是否运算
                if(op == '+') tmp.push_back(num1);
                else if(op == '-') tmp.push_back(-num1);
                else if(op == '*' || op == '/')
                {   //提取栈顶元素做运算
                    int num2 = 0;
                    if(!tmp.empty())//栈中有元素时
                    {
                        num2 = tmp.back();
                        tmp.pop_back();//删除栈顶元素
                    }
                    //将运算结果入栈
                    if(op == '*') tmp.push_back(num1*num2);
                    else if(op == '/') tmp.push_back(num2 / num1);//注意除法这里的运算顺序
                }
            }
        }

        //将栈中元素累加
        int ret = 0;
        for(auto num : tmp)
            ret += num;
        
        //返回最终运算结果
        return ret;
    }
};

  vector::back()返回引用,可直接对值修改。

reference back();
const_reference back() const;
                //2、根据该数的左侧操作符(op中保存的值),判断是否运算
                if(op == '+') tmp.push_back(num1);
                else if(op == '-') tmp.push_back(-num1);
                else if(op == '*') tmp.back() *= num1;//reference back();可直接修改值
                else if(op == '/') tmp.back() /= num1;//注意除法这里的运算顺序

  
  
  
  
  
  

5、字符串解码(medium)

  题源:链接。

在这里插入图片描述
  
  

5.1、题解

  1)、思路分析
  同上一题,使用栈模拟,分情况讨论。
  解析此题,相对复杂的是嵌套情况:2[a3[b]],需要先解码内层的3[b]获得bbb,才能对外层2[abbb]进行解码。 因此,我们使用双栈来模拟,一个用于存放数字k(重复出现的次数),一个用于存放该数字对应的字符(重复k次的字符串)。分情况讨论如下:
  1、遇到数字:提取出这个数字,放入”数字栈"中;
  2、遇到'[':把后面的字符串提取出来,放入“字符串栈”中;
  3、遇到']':可以进行一次解析,将结果放回"字符串栈"栈顶的字符串后面;
  4、遇到单独的字符:提取出来这个字符串,直接放在"字符串栈"栈顶的字符串后面

  细节:字符串这个栈中,先放入一个空串。
  
  2)、题解

class Solution {
public:
    string decodeString(string s) {
        //双栈
        stack<int> nums;//存放数字的栈
        stack<string> strs;//存放字符串的栈
        strs.push("");

        int i = 0; int len = s.size();
        //遍历,模拟过程,分情况讨论
        while(i < len)
        {
            if(isdigit(s[i]))//若当前遍历到的是数字
            {
                //因数组可能不止一位,需要提取数字,再放入数字栈中
                int k = 0;
                while(i < len && isdigit(s[i]))
                    k = k*10 + s[i++] -'0';
                nums.push(k);
            }
            else if(s[i] == '[')//若当前遍历到的是左括号
            {   
                
                ++i;//跳过该'[',提取'['之后的字符串,放入字符栈中
                string tmp;
                while(i < len && isalpha(s[i]))
                    tmp += s[i++];
                strs.push(tmp);
            }
            else if(s[i] == ']')//若遇到右括号,可进行一次解码操作
            {
                //分别提取数字栈和字符栈的栈顶元素
                int k = nums.top(); 
                nums.pop();

                string str = strs.top();
                strs.pop();

                //解码,存放入字符栈中;
                string tmp;
                while(k--) tmp += str;
                strs.top() += tmp;//value_type& top();

                ++i;//当前情况处理结束,可以跳过该']',继续后续的遍历

            }
            else if(isalpha(s[i]))//若遇到的是字符
            {
                //提取字符,将其放在栈顶元素之后。
                string tmp;
                while(i < len && isalpha(s[i]))
                    tmp += s[i++];
                strs.top() += tmp;
            }

        }
        return strs.top();
    }
};

  
  
  
  
  

6、验证栈序列(medium)

  题源:链接。

在这里插入图片描述

  
  

6.1、题解

  1)、思路分析
  用栈来模拟进出栈的流程: 一直让元素进栈,进栈的同时判断是否需要出栈。当所有元素模拟完毕之后,如果栈中还有元素,那么就是一个非法的序列。否则,就是一个合法的序列。
在这里插入图片描述

  
  2)、题解

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> st;
        int i = 0;//用于指向当前poped中待出栈元素
        for(auto x : pushed)
        {
            st.push(x);//让元素入栈
            //判断当前入栈元素是否是出栈元素
            while(!st.empty() && st.top() == popped[i])
            {
                st.pop();
                i++;
            }
        }
        return st.empty();
    }
};

  
  
  
  
  
  
  
  

Fin、共勉。

在这里插入图片描述

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

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

相关文章

Buildroot系统构建学习笔记(以百问网imx6ullPro开发板为例)

一、Builroot是什么&#xff1f; Buildroot是一组Makefile和补丁&#xff0c;可简化并自动化地为嵌入式系统构建完整的、可启动的Linux环境(包括bootloader、Linux内核、包含各种APP的文件系统)。Buildroot运行于Linux平台&#xff0c;可以使用交叉编译工具为多个目标板构建嵌…

c++|list使用及深度剖析模拟实现

目录 一、list介绍与使用 1.1 list介绍 1.2 list的使用 1.2.1list的构造 1.2.2iterator 1.2.3容量 1.2.4元素访问 1.2.5 元素修改 二、list的深度剖析及模拟实现 三、list与vector的对比 一、list介绍与使用 1.1 list介绍 ①list底层是带头双向循环链表&#xff0c;在…

Redis进阶——BitMap用户签到HyperLogLog实现UV统计

目录 用户签到实现签到功能 签到统计HyperLogLog实现UV统计UV和PV的概述测试百万数据的统计 用户签到 BitMap功能演示 我们针对签到功能完全可以通过MySQL来完成&#xff0c;例如下面这张表 用户签到一次&#xff0c;就是一条记录&#xff0c;假如有1000W用户&#xff0c;平…

RCE漏洞及其绕过——[SWPUCTF 2021 新生赛]easyrce、caidao、babyrce

目录 什么是Shell 1、Shell简介 2、印刷约定 一、什么是RCE 漏洞产生条件&#xff1a; 漏洞检测&#xff1a; 1.远程命令执行 system()函数&#xff1a; passthru()函数&#xff1a; exec()函数&#xff1a; 无回显 shell_exec()函数&#xff1a; 2.远程代码执行 e…

我们一起看看《看漫画学C++》中如何讲解对象的动态创建与销毁

《看漫画学C》这本书中会用图文的方式生动地解释对象的动态创建与销毁。在C中&#xff0c;动态创建对象是通过new运算符来实现的&#xff0c;而销毁对象则是通过delete运算符来完成的。这种方式可以让程序在需要时分配内存给对象&#xff0c;并在对象不再需要时释放内存&#x…

「Word 论文排版」插入分节符导致word转PDF后出现空白页

问题 word转PDF后出现空白页 解决 但是此方法会让页面页脚标记出错 TODO 如下图所示 在论文目录后有一个分节符&#xff0c;转成PDF之后就多了一个空白页 文件-打印-页面设置-选中封面那一页-版式-从偶数页开始 再导出空白页就没了

Nginx莫名奇妙返回了404

描述 nginx作为反向代理&#xff0c;代理python的服务&#xff0c;但是通过代理访问服务的时候&#xff0c;报了404的错误。 难受的是客户现场没有查看日志的权限&#xff0c;只有查看配置文件的权限&#xff0c;我们检测了几遍配置文件也没有找到问题&#xff0c;哎~ 问题引…

34. 【Android教程】菜单:Menu

作为 Android 用户&#xff0c;你一定见过类似这样的页面&#xff1a; 它就是我们今天的主角——菜单&#xff0c;它的使用场景和作用不用多说&#xff0c;几乎每个 App 都会用到它&#xff0c;今天我们就一起来看看 Android 提供的几种菜单类型及用法。 1. 菜单的几种类型 根…

[Algorithm][滑动窗口][无重复字符的最长字串][最大连续的一个数 Ⅲ][将x减到0的最小操作数]详细讲解

目录 1.无重复字符的最长字串1.题目链接2.算法原理详解3.代码实现 2.最大连续的一个数 Ⅲ1.题目链接2.算法原理详解3.代码实现 3.将x减到0的最小操作数1.题目链接2.算法原理详解3.代码实现 1.无重复字符的最长字串 1.题目链接 无重复字符的最长字串 2.算法原理详解 研究的对…

美化博客文章(持续更新)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;游戏实现&#xff1a;贪吃蛇​​​​​​ &#x1f337;追光的人&#xff0c;终会万丈光芒 前言&#xff1a; 该文提供我的一些文章设计的一些方法 目录 1.应用超链接 1.应用超链接

URL GET +号后台接收成空格

问题&#xff1a;参数spdmwhbs001 其中包含URL特殊符号 如果用GET请求方式不做任何不处理那么浏览器自动将转为%20 请求链接为 details?spdmwhbs%20001&limitKcysType1 后台接收到的参数为 whbs 001 &#xff0c;自动将号转成空格了。 尝试解决&#xff08;失败&#…

使用FastDDS编译IDL文件

1.安装FastDDS环境 Ubuntu22.04 1.1安装依赖的软件 sudo apt-get update //基础工具安装 sudo apt install cmake g python3-pip wget git //Asio 是一个用于网络和低级 I/O 编程的跨平台C库&#xff0c;它提供了一致的 异步模型。 TinyXML2是一个简单&#xff0c;小巧&…

400电话如何对接配置SIP

400电话对接配置SIP的基本步骤 要配置400电话对接SIP&#xff0c;通常需要遵循以下基本步骤&#xff1a; 注册和认证&#xff1a;首先需要在相应的云通信平台上注册账号&#xff0c;并进行企业实名认证。 开通语音服务&#xff1a;在通过认证后&#xff0c;需要开通语音服务&…

C# winform s7.net expected 22 bytes.”

S7.Net.PlcException:“Received 12 bytes: 32-02-00-00-00-00-00-00-00-00-81-04, expected 22 bytes.” 原因是博图的连接机制未勾选

Java编程题 | 数组元素交换

大家可以关注一下专栏&#xff0c;方便大家需要的时候直接查找&#xff0c;专栏将持续更新~ 题目描述 编写一个Java程序&#xff0c;输入一个整数数组&#xff0c;将最大的元素与第一个元素交换&#xff0c;最小的元素与最后一个元素交换&#xff0c;然后输出修改后的数组…

数据结构从入门到实战——顺序表的应用

目录 一、基于动态顺序表实现通讯录 二、代码实现 2.1 通讯录的初始化 2.2 通讯录的销毁 2.3 通讯录的展示 2.4 通讯录添加联系人信息 2.5 通讯录删除联系人信息 2.6 通讯录修改联系人信息 2.7 通讯录的查找联系人信息 2.8 将通讯录中联系人信息保存到文件中 2.9…

【Qt】设置QT标准对话框为中文字体

设置QT标准对话框为中文字体 一、问题二、解决方法1、找到Qt内置的翻译文件 qt_zh_CN.qm2、在代码中加载该文件 一、问题 在Qt中我们使用的标准对话框都是英文&#xff0c;例如下面的 字体选择对话框&#xff0c;但是实际中我们需要构建的是中文对话框。 所以我们需要使用Qt官…

【redis】hash和list常用命令

hash类型 Redis自身已经是键值对结构了。Redis自身的键值对就是通过哈希的方式来组织的。 把key这一层组织完成之后,到了value这一层。value的其中一种类型还可以再是哈希。哈希类型中的映射关系通常称为field-value,用于区分Redis整体的键值对(key-value)。注意这里的 value…

为什么创业总是小部分人能成功?

每年都有大批量应届生等待就业&#xff0c;同时&#xff0c;也有一大批大龄中青人正在面临失业。在这种焦虑&#xff0c;内卷的氛围下&#xff0c;就会激发一波又一波的人走上创业的道路。想靠创业改变命运。辞职之后&#xff0c;第一时间能想到的创业方式要么就是就是 开个奶茶…

阿里云ECS服务器安装docker

首先查看阿里云ECS的服务器的版本 cat /etc/redhat-release如果是Alibaba Cloud Linux release 3,请执行以下命令 添加docker-ce的dnf源。 sudo dnf config-manager --add-repohttps://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo安装Alibaba Cloud Linux 3专…