算法训练day11Leetcode20有效的括号1047删除字符串中所有相邻重复项150逆波兰表达式求值

今日学习的文章和视频链接

https://leetcode.cn/problems/valid-parentheses/description/
https://programmercarl.com/0020.%E6%9C%89%E6%95%88%E7%9A%84%E6%8B%AC%E5%8F%B7.html

20 有效的括号

题目描述

给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。示例 1:输入:s = "()"
输出:true
示例 2:输入:s = "()[]{}"
输出:true
示例 3:输入:s = "(]"
输出:false提示:1 <= s.length <= 104
s 仅由括号 '()[]{}' 组成

我遇到的问题

判断右括号后忘记pop

题目分析

括号匹配是使用栈解决的经典问题。

如果还记得编译原理的话,编译器在 词法分析的过程中处理括号、花括号等这个符号的逻辑,也是使用了栈这种数据结构。

由于栈结构的特殊性,非常适合做对称匹配类的题目。

首先要弄清楚,字符串里的括号不匹配有几种情况。

先来分析一下 这里有三种不匹配的情况,

  1. 第一种情况,字符串里左方向的括号多余了 ,所以不匹配。
  2. 括号没有多余,但是括号的类型没有匹配上
  3. 字符串里右方向的括号多余了,所以不匹配
    第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false

第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false

第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false

那么什么时候说明左括号和右括号全都匹配了呢,就是字符串遍历完之后,栈是空的,就说明全都匹配了。

但还有一些技巧,在匹配左括号的时候,右括号先入栈,就只需要比较当前元素和栈顶相不相等就可以了,比左括号先入栈代码实现要简单的多了!

我的acm模式代码

#include <iostream>
#include <stack>class Solution {
public:bool isValid(std::string s) {std::stack<char> st;if (s.size() % 2 != 0) return false;for (int i = 0; i < s.size(); i++) {if (s[i] == '(') {st.push(')');}else if(s[i] == '[') {st.push(']');}else if(s[i] == '{') {st.push('}');}else if(st.empty() || st.top() != s[i]) {return false;}else {st.pop();}}return st.empty();}
};int main() {std::string s = "(())[]{}";Solution sol;if(sol.isValid(s)) {std::cout << "valid string" <<std::endl;}else {std::cout << "unvalid string" << std::endl;}
}

这段C++代码的目的是检查一个只包含圆括号、方括号和花括号的字符串是否有效。它用于判断输入的字符串是否含有有效的 {}()[] 配对和顺序。

时间复杂度分析:

  1. 线性时间复杂度 (O(n)): isValid 函数遍历输入字符串 s 的每个字符一次。因此,时间复杂度与字符串的长度成正比,导致线性时间复杂度为 O(n),其中 n 是字符串的长度。

空间复杂度分析:

  1. 线性空间复杂度 (O(n)): 空间复杂度主要由栈 st 的使用决定。在最坏的情况下(例如,一个开括号的字符串),栈可能会存储几乎每个字符,直到开始弹出为止。因此,空间复杂度也是线性的,O(n),与输入字符串的长度成正比。

总的来说,考虑到线性的时间和空间复杂度,这个函数对于合理长度的字符串来说是高效的。

1047. 删除字符串中的所有相邻重复项

题目分析

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。在 S 上反复执行重复项删除操作,直到无法继续删除。在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。示例:输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。提示:1 <= S.length <= 20000
S 仅由小写英文字母组成。

我看到题目后的想法

和上1题类似,用栈来存储,进行类似消消乐,最后逆序保存栈顶元素作为输出

题目分析

我们在删除相邻重复项的时候,其实就是要知道当前遍历的这个元素,我们在前一位是不是遍历过一样数值的元素,那么如何记录前面遍历过的元素呢?

所以就是用栈来存放,那么栈的目的,就是存放遍历过的元素,当遍历当前的这个元素的时候,去栈里看一下我们是不是遍历过相同数值的相邻元素。

从栈中弹出剩余元素,此时是字符串ac,因为从栈里弹出的元素是倒序的,所以再对字符串进行反转一下,就得到了最终的结果。

这道题目就像是我们玩过的游戏对对碰,如果相同的元素挨在一起就要消除。

可能我们在玩游戏的时候感觉理所当然应该消除,但程序又怎么知道该如何消除呢,特别是消除之后又有新的元素可能挨在一起。

此时游戏的后端逻辑就可以用一个栈来实现(我没有实际考察对对碰或者爱消除游戏的代码实现,仅从原理上进行推断)。

游戏开发可能使用栈结构,编程语言的一些功能实现也会使用栈结构,实现函数递归调用就需要栈,但不是每种编程语言都支持递归,例如:

递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。

相信大家应该遇到过一种错误就是栈溢出,系统输出的异常是Segmentation fault(当然不是所有的Segmentation fault 都是栈溢出导致的) ,如果你使用了递归,就要想一想是不是无限递归了,那么系统调用栈就会溢出。

而且在企业项目开发中,尽量不要使用递归!在项目比较大的时候,由于参数多,全局变量等等,使用递归很容易判断不充分return的条件,非常容易无限递归(或者递归层级过深),造成栈溢出错误(这种问题还不好排查!)

我的acm模式代码

#include <iostream>
#include <stack>class Solution {
public:std::string removeDuplicates(std::string s) {std::stack<char> st;for (int i = 0; i < s.size(); i++) {if (st.empty() || st.top() != s[i]) {st.push(s[i]);}else {st.pop();}}std::string res;res.resize(st.size());int n = st.size();for (int i = st.size() - 1; i >= 0; i--) {res[i] = st.top();st.pop();}return res;}
};int main() {std::string s = "abbaca";Solution sol;std::string res = sol.removeDuplicates(s);std::cout << res <<std::endl;
}

我遇到的困难

自己写的时候没有进行res.resize.无法给res进行赋值

问题出在尝试向 std::string res 赋值的方式上。当使用 res[i] = st.top(); 这行代码时,假设 res 是一个空字符串,实际上在尝试访问并修改一个不存在的字符位置。在 C++ 中,这种操作是不合法的,因为在访问未被初始化或分配的内存。

解决这个问题有几种方法:

  1. 使用 push_back+= 在遍历栈时,可以使用 push_back+= 来向 res 中添加字符。例如,res.push_back(st.top());

  2. 预先分配字符串大小: 另一种方法是,在填充 res 之前,先设置它的大小。可以在将字符弹出栈之前,使用 std::stringresize 方法来设置字符串的大小。

修改后的代码示例如下:

class Solution {
public:std::string removeDuplicates(std::string s) {std::stack<char> st;for (char ch : s) {if (st.empty() || st.top() != ch) {st.push(ch);}else {st.pop();}}std::string res;res.resize(st.size()); // 设置字符串大小for (int i = st.size() - 1; i >= 0; i--) {res[i] = st.top();st.pop();}return res;}
};

或者,使用 push_back

class Solution {
public:std::string removeDuplicates(std::string s) {std::stack<char> st;for (char ch : s) {if (st.empty() || st.top() != ch) {st.push(ch);}else {st.pop();}}std::string res;while (!st.empty()) {res = st.top() + res; // 在字符串前面添加字符st.pop();}return res;}
};

这两种方法都能有效地解决无法给 res 赋值的问题。

逆波兰表达式求值

题目分析

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。请你计算该表达式。返回一个表示表达式值的整数。注意:有效的算符为 '+'、'-'、'*' 和 '/' 。
每个操作数(运算对象)都可以是一个整数或者另一个表达式。
两个整数之间的除法总是 向零截断 。
表达式中不含除零运算。
输入是一个根据逆波兰表示法表示的算术表达式。
答案及所有中间计算结果可以用 32 位 整数表示。示例 1:输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22提示:1 <= tokens.length <= 104
tokens[i] 是一个算符("+"、"-"、"*" 或 "/"),或是在范围 [-200, 200] 内的一个整数逆波兰表达式:逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。
逆波兰表达式主要有以下两个优点:去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中

我的想法

定义两个整数变量,用来存储操作数
首先遍历将数字入栈,遇到第一个符号时,将前两个数字元素出栈进行符号运算,结果再入栈,

题目分析

其实逆波兰表达式相当于是二叉树中的后序遍历。 大家可以把运算符作为中间节点,按照后序遍历的规则画出一个二叉树。

但我们没有必要从二叉树的角度去解决这个问题,只要知道逆波兰表达式是用后序遍历的方式把二叉树序列化了,就可以了。

在进一步看,本题中每一个子表达式要得出一个结果,然后拿这个结果再进行运算,那么这岂不就是一个相邻字符串消除的过程,和1047.删除字符串中的所有相邻重复项 (opens new window)中的对对碰游戏是不是就非常像了。

我们习惯看到的表达式都是中缀表达式,因为符合我们的习惯,但是中缀表达式对于计算机来说就不是很友好了。

例如:4 + 13 / 5,这就是中缀表达式,计算机从左到右去扫描的话,扫到13,还要判断13后面是什么运算符,还要比较一下优先级,然后13还和后面的5做运算,做完运算之后,还要向前回退到 4 的位置,继续做加法,你说麻不麻烦!

那么将中缀表达式,转化为后缀表达式之后:[“4”, “13”, “5”, “/”, “+”] ,就不一样了,计算机可以利用栈来顺序处理,不需要考虑优先级了。也不用回退了, 所以后缀表达式对计算机来说是非常友好的。

可以说本题不仅仅是一道好题,也展现出计算机的思考方式。

在1970年代和1980年代,惠普在其所有台式和手持式计算器中都使用了RPN(后缀表达式),直到2020年代仍在某些模型中使用了RPN。

我的acm代码

#include <iostream>
#include <vector>
#include <stack>
#include <string>
class Solution {
public:int evalRPN(std::vector<std::string>& tokens) {int a,res;if (tokens.size() == 1) {res = std::stoi(tokens[0]);return res;}std::stack<std::string> st;for (std::string s:tokens) {if (s != "+" && s != "-" && s != "*" && s != "/") {st.push(s);}else {res = std::stoi(st.top());st.pop();a = std::stoi(st.top());st.pop();if (s == "+") {res = a + res;}else if (s == "-") {res = a - res;}else if (s == "*") {res = a * res;}else if (s == "/") {res = a / res;}st.push(std::to_string(res));}}return res;}
};int main () {// std::vector<std::string> tokens = {"2","1","+","3","*"};std::vector<std::string> tokens = {"10","6","9","3","+","-11","*","/","*","17","+","5","+"};Solution sol;int res = sol.evalRPN(tokens);std::cout << res << std::endl;
}

我遇到的困难

没有考虑到只有一个元素的情况,Leetcode上有一个实例没有通过,加上以下代码

if (tokens.size() == 1) {res = std::stoi(tokens[0]);return res;}

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

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

相关文章

使用emu8086实现——显示程序设计实验

一、实验目的 1. 掌握DOS系统功能调用的编程方法 2. 掌握汇编语言程序设计方法&#xff0c;自己编写程序&#xff0c;并调试运行验证结果。 二、实验内容 1.在显示器上的输出窗口显示A-Z共26个大写英文字母。 代码&#xff1a; datas segmentzimu db "A B C D E F G H …

git入门之本地操作

1、启动git命令输入 在想要建立仓库&#xff0c;建议的方式是在文件夹中右键单击&#xff0c;选择git bash here 2、初始化仓库命令&#xff1a;git init 3、查看仓库状态&#xff1a;git status 4、更新仓库特定文件&#xff1a;git add xxx 5、更新仓库所有文件&#xff1…

LLVM代码内容

1. LLVM简介 LLVM库包含所有LLVM顶层项目&#xff0c;可以分为以下几类&#xff1a; • LLVM核心库和附加内容 • 编译器和工具 • 运行时库 LLVM是一个编译器框架。LLVM作为编译器框架&#xff0c;是需要各种功能模块支撑起来的。可以将clang和lld都看做是LLVM的组成部分。…

老卫带你学---leetcode刷题(50. Pow(x, n))

50. Pow(x, n) 问题 实现 pow(x, n) &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即&#xff0c;xn &#xff09;。 示例 1&#xff1a; 输入&#xff1a;x 2.00000, n 10 输出&#xff1a;1024.00000 示例 2&#xff1a; 输入&#xff1a;x 2.10000, n 3 输出…

RandLA-Net导出onnx模型并使用onnxruntime推理

首先下载RandLA-Net工程&#xff1a;https://github.com/tsunghan-wu/RandLA-Net-pytorch 导出onnx模型 import torch from utils.config import ConfigSemanticKITTI as cfg from network.RandLANet import Networkmodel Network(cfg) checkpoint torch.load("./pret…

Pandas实战100例 | 案例 13: 数据分类 - 使用 `cut` 对数值进行分箱

案例 13: 数据分类 - 使用 cut 对数值进行分箱 知识点讲解 在数据分析中&#xff0c;将连续的数值数据分类成不同的区间&#xff08;或“分箱”&#xff09;是一种常见的做法。Pandas 提供了 cut 函数&#xff0c;它可以根据你指定的分箱边界将数值数据分配到不同的类别中。 …

力扣(leetcode)第709题转成小写字母(Python)

709.转成小写字母 题目链接&#xff1a;709.转成小写字母 给你一个字符串 s &#xff0c;将该字符串中的大写字母转换成相同的小写字母&#xff0c;返回新的字符串。 示例 1&#xff1a; 输入&#xff1a;s “Hello” 输出&#xff1a;“hello” 示例 2&#xff1a; 输入&am…

浏览器进程模型和JS的事件循环

一、浏览器的进程模型 1、什么是进程&#xff1f; 程序运行所需要的专属内存空间 2、什么是线程&#xff1f; ​​​​​运行​代码的称为线程&#xff08;同一个进程中的线程共享进程的资源&#xff09; ⼀个进程⾄少有⼀个线程&#xff0c;所以在进程开启后会⾃动创建⼀个线…

ubuntu在使用su - root时提示认证失败

原因&#xff1a; 在ubuntu中&#xff0c;默认情况下&#xff0c;是没有开启root账户的&#xff0c;因此在输入密码的时候会显示认证失败。 解决方法&#xff1a; 输入sudo passwd root设置密码来激活root权限 参考链接&#xff1a;ubuntu出现su:Authentication failure解决方…

《剑指 Offer》专项突破版 - 面试题 7 : 数组中和为 0 的 3 个数字(C++ 实现)

题目链接&#xff1a;15. 三数之和 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 输入一个数组&#xff0c;如何找出数组中所有和为 0 的 3 个数字的三元组&#xff1f;需要注意的是&#xff0c;返回值中不得包含重复的三元组。例如&#xff0c;在数组 [-1, 0, …

【数值分析】数值微分

1. 基于Taylor公式的数值微分公式 f ′ ( x ) ≈ f ( x h ) − f ( x ) h , 截断误差 − f ′ ′ ( ξ ) 2 h f(x)\approx \frac{f(xh)-f(x)}{h}\,\,,\,\, 截断误差 \,\,\, - \frac{f(\xi)}{2}h f′(x)≈hf(xh)−f(x)​,截断误差−2f′′(ξ)​h f ′ ( x ) ≈ f ( x ) − f …

【JUC进阶】14. TransmittableThreadLocal

目录 1、前言 2、TransmittableThreadLocal 2.1、使用场景 2.2、基本使用 3、实现原理 4、小结 1、前言 书接上回《【JUC进阶】13. InheritableThreadLocal》&#xff0c;提到了InheritableThreadLocal虽然能进行父子线程的值传递&#xff0c;但是如果在线程池中&#x…

spring-mvc(1):Hello World

虽然目前大多数都是使用springboot来开发java程序&#xff0c;或者使用其来为其他端提供接口&#xff0c;而为其他端提供接口&#xff0c;这些功能都是依靠springmvc实现的&#xff0c;所以有必要学习一下spring-mvc&#xff0c;这样才能更好的学习springboot。 一&#xff0c…

c语言题目之九九乘法表的打印

文章目录 题目一、题目分析二&#xff0c;代码编写三&#xff0c;拓展 题目 用c语言打印九九乘法表 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、题目分析 在上面图我们假设一个乘法为一个单位&#xff0c;在这里我们可以看到第一行有一行一列&…

Python 3 字符串的基本使用

介绍 字符串是一个或多个字符&#xff08;字母、数字、符号&#xff09;的序列&#xff0c;可以是常量&#xff0c;也可以是变量。字符串由 Unicode 组成&#xff0c;是不可变的序列&#xff0c;这意味着它们是不变的。 由于文本是我们在日常生活中使用的一种常见数据形式&am…

掌握WPF控件:熟练常用属性(二)

WPF布局常用控件&#xff08;二&#xff09; Calendar 用于日期选择的控件。它提供了一个可视化的界面&#xff0c;可以通过它来选择特定的日期。 常用属性描述DisplayMode用来设置Calendar的显示模式&#xff0c;有三种可选值&#xff1a;默认Month&#xff08;月&#xff…

自编C++题目——输入程序

预估难度 简单 题目描述 小明编了一个输入程序&#xff0c;当用户的输入之中有<时&#xff0c;光标移动到最右边&#xff1b;当输入有>时&#xff0c;光标移动到最左边&#xff0c;当输入有^时&#xff0c;光标移动到前一个字符&#xff0c;当输入为#时&#xff0c;清…

华纳云:在Conda中环境迁移有哪些步骤

在Conda中&#xff0c;环境的迁移通常涉及两个方面&#xff1a;导出环境配置和导出环境中的包。这可以通过以下步骤来完成&#xff1a; 导出环境配置&#xff1a; 在源环境中运行以下命令&#xff0c;导出环境配置到一个 environment.yml 文件中&#xff1a; conda env expo…

SLAM第十四讲

基础知识 四元数 先将三维空间的点p(x,y,z) 变成四元数的表示q(0,x,y,z) 其中0为四元数的实部&#xff0c;x,y,z为四元数的虚部。 实部为0的四元数也叫纯虚四元数。 通过 左乘四元数&#xff…

YOLOv8 Ultralytics:使用Ultralytics框架进行SAM图像分割

YOLOv8 Ultralytics&#xff1a;使用Ultralytics框架进行SAM图像分割 前言相关介绍前提条件实验环境安装环境项目地址LinuxWindows 使用Ultralytics框架进行SAM图像分割参考文献 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容&#xf…