数据结构——栈的应用

news2024/12/24 8:09:49

数据结构——栈的应用

  • 括号匹配
  • 中缀转后缀
    • 什么是中缀后缀
      • 中缀表达式 (Infix Notation)
      • 后缀表达式 (Postfix Notation, Reverse Polish Notation, RPN)
    • 加减,乘除运算
    • 处理括号
    • 后缀转中缀

我们今天来看栈的应用:

括号匹配

栈一个经典的应用就是括号匹配:

https://leetcode.cn/problems/valid-parentheses/description/

在这里插入图片描述
在这里插入图片描述
我们遇到的字符串可能更复杂,比如像:([{{{()]]{}(}}}{}

首先我们知道的,一个左括号如果要匹配,必须要有对应的右括号,而且是匹配最近的,而且最里面的左括号优先匹配对应的右括号(从最里面开始匹配,方便我们的检测,如果从最外面开始,会比较费劲)
在这里插入图片描述
所以我们思想是这样的:

1.凡是遇到左括号就入栈
2. 一旦遇到右括号,就弹出进行对应匹配,如果匹配,则弹出,否则返回无效
3. 最后,扫描完并且栈为空,说明这是合法的括号字符串

class Solution {
public:
    bool isValid(string s) 
    {
        //判断字符串的长度
        int lenghth = s.size();
        if(lenghth % 2 != 0)
        return false;

        stack<char> st;

        //左括号入栈
        for(int i = 0; i < lenghth ; i++)
        {
            if(s[i] == '(' || s[i] == '{' || s[i] == '[')
            {
                st.push(s[i]);
            }
            else
            {
                if(st.empty()) return false;
                if (s[i] == ')' && st.top() != '(') return false;
                if (s[i] == '}' && st.top() != '{') return false;
                if (s[i] == ']' && st.top() != '[') return false;

                st.pop();
            }
        }

        return st.empty();
    }
};

如果学习过unordered_map还可以这样玩:

class Solution {
public:
    bool isValid(string s) 
    {
        int lenghth = s.size();

        if(lenghth % 2 != 0)
        return false;

        unordered_map<char, char> pairs = {
            {')', '('},
            {']', '['},
            {'}', '{'}  //形成键值对关系
        };
        
        stack<char> st;

        for(auto ch : s)
        {
            if(pairs.count(ch))
            {
                if(st.empty() || st.top() != pairs[ch])
                {
                    return false;
                }
                st.pop();
            }
            else
            {
                st.push(ch);
            }
        }
        return st.empty();   
    }


};

中缀转后缀

什么是中缀后缀

中缀表达式和后缀表达式是两种不同的数学表达式表示方法,它们在结构和计算方式上有所区别。下面分别对这两种表达式进行详细解释:

中缀表达式 (Infix Notation)

定义
中缀表达式是我们日常生活中最常用的数学表达式书写形式,其中运算符位于其作用的两个操作数之间。这种表达方式之所以被称为“中缀”,是因为运算符处于操作数的中间位置。

示例

3 + 4 * 5
(10 / 2) - 3

在这两个例子中,“+”、“*”、“/”和“-”分别是中缀运算符,它们位于相关操作数之间。

特点

  • 符合人类自然阅读和书写的习惯,直观易懂。
  • 计算中缀表达式时需要遵循特定的运算顺序规则,如先乘除后加减、同级运算符从左到右等,以及括号来改变默认优先级。
  • 由于依赖运算符优先级和结合性,计算机在处理中缀表达式时通常需要额外的解析步骤来确定正确的计算顺序。

后缀表达式 (Postfix Notation, Reverse Polish Notation, RPN)

定义
后缀表达式(又称为逆波兰表示法)是一种运算符置于其作用的操作数之后的表达式形式。在这种表示法中,不再需要括号来明确运算顺序,因为每个运算符都是在其所需的所有操作数已经出现之后才出现的。

示例

3 4 5 * +
10 2 / 3 -

这里,“*”、“+”、“/”和“-”同样是运算符,但它们现在出现在各自操作数之后。

特点

  • 后缀表达式消除了对括号的需求,因为其结构已经明确指定了运算顺序:从左到右扫描表达式,遇到数字时直接压入堆栈,遇到运算符时弹出堆栈中足够的操作数进行计算,并将结果压回堆栈。
  • 计算机可以直接按照从左到右的顺序遍历来计算后缀表达式,无需复杂的优先级判断,因此非常适合机器处理,尤其是在需要高效计算的场合。
  • 对于人类阅读者来说,后缀表达式不如中缀表达式直观,但其计算逻辑简洁且易于编程实现。

转换与计算
将中缀表达式转换为后缀表达式通常涉及使用栈数据结构来处理运算符的优先级和结合性。转换完成后,后缀表达式可以直接通过一个简单的栈机制进行计算,无需复杂的解析过程。

总结来说,中缀表达式是人们习惯的数学书写方式,直观易读但计算时需考虑运算符优先级;而后缀表达式虽然对人不太直观,但其结构清晰、计算简单,特别适用于计算机自动化处理。

现在给你一个中缀表达式要求你转成后缀表达式:

https://www.nowcoder.com/practice/4e7267b55fdf430d9403aa12206572b3?tpId=182&tqId=34663&ru=/exam/oj

在这里插入图片描述
我们先从简单的开始:
在这里插入图片描述

加减,乘除运算

加减,乘除运算,乘除运算是比加减运算级高的,所以我们在入栈的时候,要考虑运算符的优先顺序:
在这里插入图片描述
还是和之前一样,如果为操作数,直接打印:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这个时候,- 号的优先级和+号一样,说明在此时+可以完成了动作,所以出栈:
在这里插入图片描述
之后继续向后走:
在这里插入图片描述

这个时候遇到了乘号,比栈顶元素的优先级高,先入栈
在这里插入图片描述

当前操作符的优先级高于栈顶运算符时,表明栈顶运算符应当在其之后完成计算。因此,为了保持正确的运算顺序,应该先将当前高优先级运算符压入栈中,待将来可能遇到的更低优先级运算符(或者遇到栈顶运算符的右括号对)时再将其弹出。这样确保了高优先级运算符始终晚于低优先级运算符出栈,符合后缀表达式的构建规则。

之后重复上面的步骤,我们就可以得到完整的后缀表达式。总结一下:

  1. 初始化一个空的栈stk,用于存储操作符和左括号。
  2. 从左到右扫描中缀表达式的每个字符。
  • 遇到操作数,将其添加到输出字符串中。
  • 遇到操作符(+、-、*、/),则将其与栈stk顶的操作符进行优先级比较:
    a. 如果栈stk为空,则将操作符压入栈stk。
    b. 如果操作符的优先级高于栈顶操作符,则将操作符压入栈stk。
    c. 如果操作符的优先级小于等于栈顶操作符,则将栈顶操作符弹出并添加到输出字符串中,然后重复步骤4。
    当扫描完中缀表达式后,将栈stk中剩余的操作符依次弹出并添加到输出字符串中。
#include<iostream>
#include<string>
#include<stack>
#include <cctype>
#include<unordered_map>

using namespace std;

// 定义一个无序映射(哈希表),用于存储运算符及其优先级
unordered_map<char, int> pairs = {
        {'+', 1}, // 加法优先级为1
        {'-', 1}, // 减法优先级为1
        {'*', 2}, // 乘法优先级为2
        {'/', 2}  // 除法优先级为2
};

// 函数声明:将输入的中缀表达式字符串转换为后缀表达式字符串
string infixToPostfix(const string &infix)
{
    // 定义一个字符栈stk用于存储运算符
    stack<char> stk;
    // 初始化后缀表达式字符串postfix
    string postfix;

    // 遍历输入的中缀表达式字符串中的每个字符
    for (char ch : infix)
    {
        // 如果字符为字母或数字(即操作数),直接添加到后缀表达式字符串中
        if (isalnum(ch))
        {
            postfix += ch;
        }
        // 否则,假设当前字符为运算符
        else
        {
            // 当栈非空,且当前运算符优先级小于等于栈顶运算符优先级时
            while (!stk.empty() && pairs[ch] <= pairs[stk.top()])
            {
                // 将栈顶运算符弹出并添加到后缀表达式字符串中
                postfix += stk.top();
                stk.pop();
            }
            // 将当前运算符压入栈中
            stk.push(ch);
        }
    }

    // 当栈非空时,依次弹出栈顶运算符并添加到后缀表达式字符串中
    while (!stk.empty())
    {
        postfix += stk.top();
        stk.pop();
    }

    // 返回生成的后缀表达式字符串
    return postfix;
}

int main()
{
    // 定义并初始化中缀表达式字符串infix
    string infix;
    cout << "请输入中缀表达式: ";
    cin >> infix;
    
    // 调用infixToPostfix函数将输入的中缀表达式转换为后缀表达式,并存储在postfix变量中
    string postfix = infixToPostfix(infix);

    // 输出转换后的后缀表达式
    cout << "后缀表达式为: "<< postfix<< endl;

    // 程序结束,返回0
    return 0;
}

在这里插入图片描述
在这里插入图片描述

处理括号

如果出现括号,我们可以理解为:括号可以提升优先等级,所以左括号直接入栈,直到遇到右括号,再依次弹出,直到把左括号也弹出栈

#include<iostream>
#include<string>
#include<stack>
#include <cctype>
#include<unordered_map>

using namespace std;

// 定义一个无序映射(哈希表),用于存储运算符及其优先级
unordered_map<char, int> pairs = {
        {'+', 1}, // 加法优先级为1
        {'-', 1}, // 减法优先级为1
        {'*', 2}, // 乘法优先级为2
        {'/', 2}  // 除法优先级为2
};

// 函数声明:将输入的中缀表达式字符串转换为后缀表达式字符串
string infixToPostfix(const string &infix)
{
    // 定义一个字符栈stk用于存储运算符和括号
    stack<char> stk;
    // 初始化后缀表达式字符串postfix
    string postfix;

    // 遍历输入的中缀表达式字符串中的每个字符
    for (char ch : infix)
    {
        // 如果字符为字母或数字(即操作数),直接添加到后缀表达式字符串中
        if (isalnum(ch))
        {
            postfix += ch;
        }
        // 处理左括号:直接压入栈中
        else if (ch == '(')
        {
            stk.push(ch);
        }
        // 处理右括号:
        else if (ch == ')')
        {
            // 当栈非空,且栈顶元素不是左括号时,不断弹出栈顶元素并添加到后缀表达式字符串中
            while (!stk.empty() && stk.top() != '(')
            {
                postfix += stk.top();
                stk.pop();
            }
            // 弹出匹配的左括号(不添加到后缀表达式字符串)
            stk.pop();
        }
        // 处理其他运算符(非括号):
        else
        {
            // 当栈非空,且当前运算符优先级小于等于栈顶运算符优先级时
            while (!stk.empty() && pairs[ch] <= pairs[stk.top()])
            {
                // 将栈顶运算符弹出并添加到后缀表达式字符串中
                postfix += stk.top();
                stk.pop();
            }
            // 将当前运算符压入栈中
            stk.push(ch);
        }
    }

    // 当栈非空时,依次弹出栈顶元素并添加到后缀表达式字符串中(此时栈中仅剩运算符)
    while (!stk.empty())
    {
        postfix += stk.top();
        stk.pop();
    }

    // 返回生成的后缀表达式字符串
    return postfix;
}

int main()
{
    // 定义并初始化中缀表达式字符串infix
    string infix;
    cout << "请输入中缀表达式: ";
    cin >> infix;
    
    // 调用infixToPostfix函数将输入的中缀表达式转换为后缀表达式,并存储在postfix变量中
    string postfix = infixToPostfix(infix);

    // 输出转换后的后缀表达式
    cout << "后缀表达式为: "<< postfix<< endl;

    // 程序结束,返回0
    return 0;
}

在这里插入图片描述

后缀转中缀

后缀转中缀思路就是逆着上面的思路,力扣上面有一道类似的题:

https://leetcode.cn/problems/8Zf90G/description/

在这里插入图片描述

力扣上面有比较全面的解析,这里不再赘述,大家可以去看官方解答:

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> stk;
        int n = tokens.size();
        for (int i = 0; i < n; i++) {
            string& token = tokens[i];
            if (isNumber(token)) {
                stk.push(atoi(token.c_str()));
            } else {
                int num2 = stk.top();
                stk.pop();
                int num1 = stk.top();
                stk.pop();
                switch (token[0]) {
                    case '+':
                        stk.push(num1 + num2);
                        break;
                    case '-':
                        stk.push(num1 - num2);
                        break;
                    case '*':
                        stk.push(num1 * num2);
                        break;
                    case '/':
                        stk.push(num1 / num2);
                        break;
                }
            }
        }
        return stk.top();
    }

    bool isNumber(string& token) {
        return !(token == "+" || token == "-" || token == "*" || token == "/");
    }
};

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

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

相关文章

前端如何将接口传来的列表数据(数组)直接下载成csv文件

前言&#xff1a;最近遇到一个需求&#xff0c;需要实现一个下载表格数据的操作&#xff0c;一般来说是前端请求后端的下载接口&#xff0c;将文件流下载下来&#xff0c;但是因为这个项目任务时间比较紧&#xff0c;后端没时间做下载接口&#xff0c;所以暂时由前端直接调列表…

[iOS]使用CocoaPods发布公开库

1.检查库名是否已被占用 选择库名时&#xff0c;尽量选择具有描述性并且独特的名字&#xff0c;这不仅可以避免命名冲突&#xff0c;还可以帮助用户更好地理解库的用途和功能。 在实际创建和发布 CocoaPods 库之前&#xff0c;确实应该检查库名是否已经被占用&#xff0c;以避…

C++ | Leetcode C++题解之第58题最后一个单词的长度

题目&#xff1a; 题解&#xff1a; class Solution { public:int lengthOfLastWord(string s) {int index s.size() - 1;while (s[index] ) {index--;}int wordLength 0;while (index > 0 && s[index] ! ) {wordLength;index--;}return wordLength;} };

HTTP:强缓存优化实践

强缓存&#xff1a;浏览器不会向服务器发送任何请求&#xff0c;直接从本地缓存中读取文件 强缓存是指浏览器在向服务器请求资源时&#xff0c;判断本地是否存在该资源的缓存&#xff0c;并判断是否过期。 如果本地缓存未过期&#xff0c;浏览器就直接使用本地缓存&#xff0c…

Threejs制作服务器机房冷却结构

这节再绘制一个机房的结构&#xff0c;因为内容比较简单&#xff0c;就只使用一个章节来介绍&#xff0c; 先来一张效果图&#xff0c; 需要两个模型&#xff1a;一个冷却设备&#xff0c;一个服务器机箱&#xff0c;我这里是从网上找来的&#xff0c;首先我们搭建一个场景&a…

AWTK 集成 OGRE 3D 图形引擎

本项目演示了如何在 AWTK 中集成 OGRE3D。 0. 准备 先编译 AWTK, 并在 env.sh 中设置 awtk 的路径。需要安装 cmake 1. 生成资源 python scripts/update_res.py all2. 编译 设置环境变量 source env.sh source env_rt.sh编译 ogre 库 cd 3rd build_assimp.sh build_ogre.…

我们自己的芯片指令集架构——龙芯架构简介

CPU指令集架构&#xff08;ISA, Instruction Set Architecture&#xff09; CPU指令集架构是处理器硬件与软件之间的接口规范&#xff0c;它定义了一组基本指令&#xff0c;以及这些指令的操作格式、编码方式、寻址模式、寄存器组织、中断机制、异常处理等各个方面。ISA是计算…

uniApp+Vue3+vite+Element UI或者Element Plus开发学习,使用vite构建管理项目,HBuilderX做为开发者工具

我们通常给小程序或者app开发后台时&#xff0c;不可避免的要用到可视化的数据管理后台&#xff0c;而vue和Element是我们目前比较主流的开发管理后台的主流搭配。所以今天石头哥就带大家来一起学习下vue3和Element plus的开发。 准备工作 1&#xff0c;下载HBuilderX 开发者…

Camera设备上晶体晶振的应用

IPC行业现状的分析 IP Camera起源于20世纪90年代&#xff0c;最早应用于监控系统中。 随着AI技术、云服务以及IoT技术的发展&#xff0c;IPC已经从安防监控向智慧城市、智能家居、自动驾驶、智能医疗等行业领域拓展&#xff0c;不单单传统地应用于工业&#xff0c;个人和家庭的…

《QT实用小工具·四十八》趣味开关

1、概述 源码放在文章末尾 该项目实现了各种样式的趣味开关&#xff1a; 1、爱心形状的switch开关&#xff0c;支持手势拖动、按压效果 2、线条样式的3种开关 项目demo演示如下所示&#xff1a; 使用方式&#xff1a; 1、sapid_switch文件夹加入工程&#xff0c;.pro文件中…

ActiveMQ 反序列化漏洞 (CVE-2015-5254)

一、漏洞描述 Apache ActiveMQ 是由美国阿帕奇&#xff08;Apache&#xff09;软件基金会开发的开源消息中间件&#xff0c;支持 Java 消息服务、集群、Spring 框架等。属于消息队列组件(消息队列组件&#xff1a;分布式系统中的重要组件&#xff0c;主要解决应用耦合、异步消息…

基于SpringBoot+Vue大学生兼职管理系统的设计与实现

目录 一、前言介绍 二、功能需求 三、功能结构设计 四、管理员功能实现 招聘单位管理 用户管理 论坛管理 公告信息管理 五、招聘单位功能实现 职位招聘管理 职位留言管理 简历投递管理 六、用户功能实现 在线论坛 职位招聘信息 简历投递 简历 七、部分核心代码 …

深入Rust标准库:必备的Rust语言高级指南

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

windows rabbitMq安装

一、Erlang 环境准备 下载安装包 跟我们跑java项目&#xff0c;要装jdk类似。rabbitMQ是基于Erlang开发的&#xff0c;因此安装rabbitMQ服务器之前&#xff0c;需要先安装Erlang环境。 官网直接下载windows直装版本&#xff1a;https://www.erlang.org/downloads 无脑安装&a…

用自然语言来编程GitHub Copilot;提高代码质量开源工具GPTLint;LLMs开源医学Meditron

✨ 1: GitHub Copilot Workspace 以Copilot 为中心的创新开发者环境&#xff0c;让开发者能用自然语言从构思到编码再到软件开发。 GitHub Copilot Workspace是一个以Copilot为中心的开发者环境&#xff0c;旨在使用自然语言从构思、编码到软件开发的全过程中提供帮助。它标志…

一、OSPF基础

目录 1.路由协议的优先级 2.转发原则&#xff1a;最长匹配原则 3.负载分担 4.路由备份&#xff08;浮动路由&#xff09; 5.路由协议的分类 6.动态路由 7.距离矢量路由协议&#xff08;BGP&#xff0c;RIP&#xff09; 8.链路状态路由协议&#xff08;OSPF&#xff0c;I…

指纹浏览器:网络安全与隐私的新工具

在互联网时代&#xff0c;隐私和网络安全成为人们越来越关注的话题。随着数字化的发展&#xff0c;个人信息的泄露和在线追踪的问题愈发严峻。在这个背景下&#xff0c;"指纹浏览器"作为一种新型工具&#xff0c;开始受到关注。撸空投需要了解指纹浏览器。本文将深入…

计算机丢失d3dx9_42.dll怎么解决,最新的5种修复教程来了

在我们日常频繁地使用电脑进行工作或是享受电子游戏带来的乐趣时&#xff0c;偶尔会遇到一个令人稍感困扰的问题&#xff0c;其中之一就是“d3dx942.dll丢失”。这个错误通常出现在运行某些游戏或应用程序时&#xff0c;它会导致程序无法正常运行。为了解决这个问题&#xff0c…

【无标题】场外个股期权多少钱才能做?个人能做吗?

场外个股期权的交易门槛相对较高&#xff0c;主要面向符合特定条件的机构投资者。一般来说&#xff0c;法人或合伙企业等组织参与的&#xff0c;需要满足最近1年末净资产不低于5000万元人民币、金融资产不低于2000万元人民币的条件&#xff0c;并具备3年以上证券、基金、期货、…

《欢乐钓鱼大师》攻略:新手小白如何快速上手?

欢迎来到《钓乐乐》世界&#xff01;这是一个充满乐趣和挑战的钓鱼游戏&#xff0c;本指南将为您提供一系列宝贵的技巧和建议&#xff0c;帮助您成为一名优秀的钓手。无论您是初次尝试还是已经有一定经验&#xff0c;都将从中获益匪浅。 一、精打细算金币 刚开始时金币总是紧缺…