算法中栈的应用

目录

练习1:删除字符串中的所有相邻重复项

练习2:比较含退格的字符串

练习3:基本计算器II

练习4:字符串解码


是一种常见的数据结构,遵循先进后出(LIFO)的原则(最后进入栈的元素将被最先移出)

栈有两种基本操作:压栈(push)出栈(pop)

压栈:将元素放入栈顶,同时栈顶指针向上移动。

出栈:将栈顶元素移出,并将栈顶指针向下移动。

栈在算法题目中的应用非常广泛,能够简化问题的处理过程,提高算法的效率,在解决算法题目时,合理使用栈能够帮助我们更好的解决问题

接下来,我们通过几道练习来进一步体会栈在算法中的应用

练习1:删除字符串中的所有相邻重复项

题目链接:

1047. 删除字符串中的所有相邻重复项 - 力扣(LeetCode)

题目描述:

给出由小写字母组成的字符串 S重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

示例:

输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。

提示:

  1. 1 <= S.length <= 20000
  2. S 仅由小写英文字母组成。 

思路分析:当出现两个相邻且相同的字母时,需要将其删除。接下来,我们模拟删除字符串中所有相邻重复项的过程:

若当前字母与与前一位字母相同时,需要将当前字母和前一位字母都删掉,然后再继续遍历下一个字母,若我们使用容器来存储符合条件的字母:

此时,我们可以发现:每次放入元素之前,先与最后一个元素相比较,若相同,则放入;若不同,则不放入,且删除最后一个元素。 此时,后放入的元素先拿出,满足 后进先出,因此,我们可以使用 栈 来帮助我们实现相邻重复项的删除

我们可以直接使用 Java 提供的 Stack 类,但由于这道题较为简单,因此,我们可以直接使用数组 来模拟实现栈

若栈为空,直接入栈

若栈不为空,比较当前元素和栈顶元素,若相同,弹出栈顶元素;若不同,将当前元素放入栈中

代码实现:

class Solution {
    public String removeDuplicates(String s) {
        StringBuffer ret = new StringBuffer();//用数组来模拟栈结构
        char[] chs = s.toCharArray();
        for(char ch: chs){//遍历数组
            if(ret.length() > 0 && ch == ret.charAt(ret.length() - 1)){
                ret.deleteCharAt(ret.length() - 1);//出栈
            }else {
                ret.append(ch);//进栈
            }
        }
        return ret.toString();
    }
}

练习2:比较含退格的字符串

题目链接:

844. 比较含退格的字符串 - 力扣(LeetCode)

题目描述:

给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。

注意:如果对空文本输入退格字符,文本继续为空。

示例 1:

输入:s = "ab#c", t = "ad#c"
输出:true
解释:s 和 t 都会变成 "ac"。

示例 2:

输入:s = "ab##", t = "c#d#"
输出:true
解释:s 和 t 都会变成 ""。

示例 3:

输入:s = "a#c", t = "b"
输出:false
解释:s 会变成 "c",但 t 仍然是 "b"。

提示:

  • 1 <= s.length, t.length <= 200
  • s 和 t 只含有小写字母以及字符 '#'

思路分析:

 # 代表退格符,当出现 # 时,删除 # 前的字符。因此,我们同样可以使用栈来帮助我们删除字符。我们可以将给定的字符串中的退格符和需要删除的字符都删除,得到字符串的最终结果,然后再比较两字符串得到的结果是否相同,同样的,我们也可以使用数组来实现栈

我们遍历字符串:

若当前字符是退格符,且栈不为空,弹出栈顶元素

若当前字符是普通字符,将当前字符入栈

代码实现:

class Solution {
    public boolean backspaceCompare(String s, String t) {
        return changeStr(s).equals(t);//比较两字符串删除退格和相应字符后是否相同
    }
    
    public String changeStr(String s){
        StringBuffer ret = new StringBuffer();
        for(int i = 0; i < s.length(); i++){
            char ch = s.charAt(i);
            if(ch != '#'){//普通字符,入栈
                ret.append(ch);
            }else{
                if(ret.length() > 0){//若栈不为空,出栈
                    ret.deleteCharAt(ret.length() - 1);
                }
            }
        }
        return ret.toString();
    }
}

练习3:基本计算器II

题目链接:

227. 基本计算器 II - 力扣(LeetCode)

题目描述:

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

整数除法仅保留整数部分。

你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。

注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。

示例 1:

输入:s = "3+2*2"
输出:7

示例 2:

输入:s = " 3/2 "
输出:1

示例 3:

输入:s = " 3+5 / 2 "
输出:5

提示:

  • 1 <= s.length <= 3 * 105
  • s 由整数和算符 ('+', '-', '*', '/') 组成,中间由一些空格隔开
  • s 表示一个 有效表达式
  • 表达式中的所有整数都是非负整数,且在范围 [0, 231 - 1] 内
  • 题目数据保证答案是一个 32-bit 整数

思路分析:

由于乘除运算优于加减运算,因此要先计算表达式中的乘除运算。因此,我们可以先计算表达式中的乘除,然后将得到的结果放回原位置,最后进行加减运算,即可得出结果:

 

因此,我们可以使用一个栈来保存进行乘除运算后的值:

当运算符为加号时,直接将加号后的元素num放入栈中

当运算符是减号时,我们将减号后的元素num的相反数放入栈中(放入 -num),这样,当我们在最后进行加减运算时,只需要进行加法运算,就可以得出结果

当运算符是乘号时,我们将栈顶元素弹出,并与乘号后元素num相乘,然后再将结果放入栈中

当运算符是除号时,我们将栈顶元素弹出,与除号后元素num相除,然后再将结果放入栈中

由于 s是一个有效表达式,因此,我们不用考虑当运算符为乘除时,栈为空的情况

但由于第一个元素前无运算符,因此,我们可以将其之前的运算符设置为 +,这样,就可将其直接放入栈中

在提取运算符之后的数字时,若数字为 123 或 45 等多位数,我们需要将数字全部提取出来,此时应该如何提取呢?

我们将num初始化为0,然后向后遍历,每遍历到一个数字k,让num * 10 + k,这样,就可以提取出每一位数字(例如:需要提取 123,当遍历到1时,num = 0*10 + 1,遍历到2时,num = 1*10 + 2,遍历到3时,num = 12*10 + 3)

因此,我们使用变量op来记录每个数字之前的运算符,op初始化为 + (让第一个数字直接入栈),然后遍历字符串:

若op为 + :数字入栈

若op为 - :数字的相反数入栈

若op为 * 或 /:弹出栈顶元素,并与数字进行计算,然后将计算结果压入栈中

注意:由于提示中告诉我们表达式中存在空格,因此,我们要注意处理空格

代码实现:

class Solution {
    public int calculate(String s) {
        Deque<Integer> stack = new ArrayDeque<>();
        char op = '+';
        int n = s.length(), i = 0;
        while(i < n){//遍历字符串
            if(s.charAt(i) == ' '){//若当前字符为空格,继续向后遍历
                i++;
            }else if(Character.isDigit(s.charAt(i))){//若当前字符为数字,提取数字
                int num = 0;
                while(i < n && Character.isDigit(s.charAt(i))){
                    num = num * 10 + (s.charAt(i) - '0');
                    i++;
                }
                //根据op进行操作
                if(op == '+') stack.push(num);
                else if(op == '-') stack.push(-num);
                else if(op == '*') stack.push(stack.pop() * num);
                else stack.push(stack.pop() / num);
            }else{//若当前字符为运算符,更新op
                op = s.charAt(i);
                i++;
            }
        }
        int ret = 0;
        //将所有元素弹出,相加,计算最终结果
        while (!stack.isEmpty()) {
            ret += stack.pop();
        }
        return ret;
    }
}

练习4:字符串解码

题目链接:

394. 字符串解码 - 力扣(LeetCode)

题目描述:

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

示例 1:

输入:s = "3[a]2[bc]"
输出:"aaabcbc"

示例 2:

输入:s = "3[a2[c]]"
输出:"accaccacc"

示例 3:

输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"

示例 4:

输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"

提示:

  • 1 <= s.length <= 30
  • s 由小写英文字母、数字和方括号 '[]' 组成
  • s 保证是一个 有效 的输入。
  • s 中所有整数的取值范围为 [1, 300] 

思路分析:题目要求我们出现 k[str]时,将[]中的字符串str重复k次, 因此:

当出现数字时,需要提取数字

当出现 [ 时,需要提取 [ 后的字符串

当出现 ] 时,需要将 [] 中的字符串重复k次

因此,我们可以使用 两个栈 来分别保存 数字 和 字符串:

当出现数字时,提取数字,放入保存数字的栈nums中(提取方法与 练习3:基本计算器II 中提取数字相同)

当出现 [ 时,将[ 后的字符串放入保存字符串的栈stack中

当出现 ] 时,将 stack 的栈顶元素 和 nums 栈顶元素 弹出,进行复制,然后再将复制结果放入stack中

但字符串中可能出现 嵌套的情况(如:3[a2[c]])或普通字符(即不用进行重复操作的字符,如:ab2[c]中的ab)

如何处理嵌套呢?

以3[a2[c]]为例,若当前字符为[,我们提取 [ 后的字符串a,放入stack中,继续遍历,

此时出现数字,提取数字3,放入nums中,

然后又遇到 [,此时提取字符串c,放入stack 中,

当遇到 ] 时,我们将stack栈顶元素c弹出、nums栈顶元素2弹出,将c重复2次,然后将其拼接到此时的栈顶元素a后,即 将 cc 拼接到 a 后,然后将 acc 放入栈中,

当遇到 ] 时,将stack栈顶元素acc弹出、nums栈顶元素3弹出,将acc重复3次,此时stack为空,直接将其压入stack中

同理,我们以类似的思路来处理普通字符,当出现普通字符时,若stack为空,我们直接将其放入栈中;若栈不为空,我们将其拼接到栈顶元素后

由于我们在每次将字符串进行复制后进行拼接,或拼接普通字符时都有判断stack是否为空,因此,我们可以先在栈stack中存放一个空串,这样,就不需要每次都判断字符串是否为空了

代码实现:

class Solution {
    public String decodeString(String s) {
        char[] chs = s.toCharArray();
        Stack<StringBuffer> stack = new Stack<>();
        Stack<Integer> nums = new Stack<>();
        int i = 0, n = chs.length;
        stack.push(new StringBuffer());
        while(i < n){
            if(chs[i] >= '0' && chs[i] <= '9'){//当前字符为数字,提取数字,并将其放入栈nums中
                int num = 0;
                while(i < n && (chs[i] >= '0' && chs[i] <= '9')){
                    num = num * 10 + (chs[i] - '0');
                    i++;
                }
                nums.push(num);
            }else if(chs[i] == '['){//当前字符为[,提取字符串,并将其放入栈stack中
                i++;
                StringBuffer tmp = new StringBuffer();
                while(i < n && (chs[i] >= 'a' && chs[i] <= 'z')){
                    tmp.append(chs[i]);
                    i++;
                }
                stack.push(tmp);
            }else if(chs[i] == ']'){//当前字符为 ] 将k个该字符串拼接到栈顶元素后
                StringBuffer tmp = stack.pop();
                int k = nums.pop();
                while(k-- > 0){
                    stack.peek().append(tmp);
                }
                i++;
            }else{//当前字符为普通字符,将所有普通字符拼接到stack栈顶元素后
                StringBuffer tmp = new StringBuffer();
                while(i < n && (chs[i] >= 'a' && chs[i] <= 'z')){
                    tmp.append(chs[i]);
                    i++;
                }
                stack.peek().append(tmp);
            }
        }
        return stack.peek().toString();
    }
}

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

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

相关文章

Linux系统:内核参数调优

目录 1、/proc目录 2、sysctl命令 3.1 控制源路由验证 3.2 控制内核的系统请求调试功能 3.3 控制核心转储是否将PID附加到核心文件名 3.4 控制TCP同步cookie的使用 3.5 在网桥上禁用netfilter 3.6 控制消息队列的默认最大大小 3.7 调试TCP内核参数 3.8 调试套…

解读:DUSt3R: Geometric 3D Vision Made Easy

概述&#xff1a;给定一个无约束图像集&#xff0c;即一组具有未知相机姿态和内在特征的照片&#xff0c;我们提出的 DUSt3R 方法会输出一组相应的点阵图&#xff0c;从中我们可以直接恢复通常难以一次性估算的各种几何量&#xff0c;如相机参数、像素对应关系、深度图和完全一…

微信小程序-2

数据绑定 index.js Page({data: {info: hello world,randomNumber: Math.random() * 10,imgSrc:http://www.itheima.com/images/logo.png} })index.wxml <view>{{ info }}</view><view>{{ randomNumber > 5 ? 随机数大于等于5 : 随机数小于5 }}</v…

HarmonyOS—HAP唯一性校验逻辑

HAP是应用安装的基本单位&#xff0c;在DevEco Studio工程目录中&#xff0c;一个HAP对应一个Module。应用打包时&#xff0c;每个Module生成一个.hap文件。 应用如果包含多个Module&#xff0c;在应用市场上架时&#xff0c;会将多个.hap文件打包成一个.app文件&#xff08;称…

JeecgBoot Vue3前端项目性能优化按需加载方案

JeecgBoot vue3前端项目在 3.5.5 版本之前&#xff0c;的确存在很严重的性能问题&#xff0c;大家可以参考以下文档进行升级。 按需加载改造方法 1、全局注册地方去掉2、组件改成异步注册3、用不到的大组件可以删掉 【精简项目方案】 大组件 1、富文本 tinyme2、Markdown3、…

深度学习算法优化流程

深度学习算法的一般优化流程&#xff0c;具体的实施方法和步骤可能会根据具体任务和数据的特点而有所不同&#xff0c;优化流程通常包括以下几个主要步骤&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作…

中文文本分类_1(pytorch 实现)

import torch import torch.nn as nn import torchvision from torchvision import transforms, datasets import os, PIL, pathlib, warningswarnings.filterwarnings("ignore") # 忽略警告信息# win10系统 device torch.device("cuda" if torch.cuda.i…

(1)预处理

我们需要的文件结构如上 main.cpp add.h add.cpp add.h 这里使用riscv的工具链编译为.i文件&#xff0c;需要使用-E&#xff0c;就是只进行预处理&#xff0c;我们可以得到两个.i文件即main.i和add.i main.i 这里看到main.i里头文件全部替换&#xff0c;然后多了三万多行 所以…

【C++第三课 - 类和对象中】构造函数、析构函数、拷贝构造函数

目录 类的6个默认成员函数构造函数自己写的构造函数默认生成的构造函数 析构函数概念特征 拷贝构造函数特征 运算符重载 、 >、 < 赋值重载Date类的完善构造函数的完善用复用 类的6个默认成员函数 默认成员函数&#xff1a;不写编译器也会默认生成一份 构造函数 自己…

uniapp制作--简单的tab切换

一、实现思路 在UniApp中&#xff0c;可以使用v-if来控制Tab栏并进行切换。 创建一个方法来控制点击时的效果。 二、实现步骤 ①view部分展示 <!-- tab选项 --><view class"select-area"><view class"select-top"><view clas…

恒创科技2024开年采购海外服务器配置及价格汇总

喜迎龙年&#xff0c;开年采购开好局。值企业开工采购浪潮来袭之际&#xff0c;为进一步满足个人开发者到中小企业等各类型用户的选购需求&#xff0c;中国香港及亚太数据中心领先服务商恒创科技启动了“ 2024 开年采购开好局”大促活动&#xff0c;该活动已于 3 月 5 日正式上…

消费品亚马逊化学测试要求有哪些?RSL,双酚A(BPA)REACH,CPSIA等

消费品亚马逊化学测试要求有&#xff1a;RSL&#xff0c;双酚A&#xff08;BPA&#xff09;REACH&#xff0c;CPSIA&#xff0c;加州65&#xff0c;等 亚马逊对所有在其官方网站上架产品的化学物质和重金属限制&#xff0c;以及亚马逊如何检查符合欧盟和美国的消费品法规中的第…

【科研基础】插图摘录

FedSL: Federated Split Learning for Collaborative Healthcare Analytics on Resource-Constrained Wearable IoMT Devices Blockchain-Based Trustworthy and Efficient Hierarchical Federated Learning for UAV-Enabled IoT Networks

langchain学习笔记(十一)

关于langchain中的memory&#xff0c;即对话历史&#xff08;message history&#xff09; 1、 Add message history (memory) | &#x1f99c;️&#x1f517; Langchain RunnableWithMessageHistory&#xff0c;可用于任何的chain中添加对话历史&#xff0c;将以下之一作为…

怎么采集GBK或GB2312等特殊字符编码的网站数据

如果要采集的网站是GBK或GB2312等特殊字符编码&#xff0c;采集结果可能是一堆看不懂的文字或乱码&#xff0c;无法使用。 通常网页文章采集工具有字符编码选项&#xff0c;默认是UTF-8&#xff08;现在大部分网站都是&#xff09;&#xff0c;改选为GBK或GB2312字符编码即可&…

c++数据结构算法复习基础-- 3 --线性表-单向链表-笔试面试常见问题

1、单链表逆序 思路图 代码实现 //著: 链表结构里记得加 friend void ReverseLink(Clink& link); void ReverseLink(Clink& link) {Node* p link.head_->next_;while( p nullptr){return;}Node* q p->next_;link.head_->next_ nullptr;while(p ! nullpt…

Java解决杨辉三角

Java解决杨辉三角 01 题目 给定一个非负整数 *numRows&#xff0c;*生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]示例 2: 输入: numRo…

亚信安慧AntDB:编织数据丝路,缔造创新篇章

亚信安慧AntDB作为一款具备国产化升级改造经验的数据库系统&#xff0c;在15年的平稳运行中积累了丰富经验。通过持续的创新和技术进步&#xff0c;AntDB不断优化性能和功能&#xff0c;满足用户的需求&#xff0c;与国际先进数据库系统保持竞争力。 AntDB秉承着与用户和行业保…

结合大象机器人六轴协作机械臂myCobot 280 ,解决特定的自动化任务和挑战!(下)

Limo Pro 小车建图导航 引言 前景提要&#xff1a;我们在上文介绍了使用LIMO cobot 实现一个能够执行复杂任务的复合机器人系统的应用场景的项目&#xff0c;从以下三个方面&#xff1a;概念设计、系统架构以及关键组件。 本文主要深入项目内核的主要部分&#xff0c;同样也主要…

python自动化管理和zabbix监控网络设备(zabbix部署监控网络设备以及验证部分)

目录 前言 一、Zabbix搭建 二、FW1 三、python脚本 四、core-sw1 五、core-sw2 六、DMZ-sw1 前言 详细配置视频解析访问&#xff1a;白帽小丑的个人空间-白帽小丑个人主页-哔哩哔哩视频 一、Zabbix搭建 sed -i s/SELINUXenforcing/SELINUXdisable/ /etc/selinux/config…
最新文章