栈的最后表演:逆波兰表达式求值

前言

今天刷题遇到了逆波兰表达式,死亡的记忆突然开始攻击我,好嘛,既然根基不牢,那么就一次性给他搞明白了!

一、算术表达式求值

算术表达式又叫中缀表达式,如果直接给出一个中缀表达式让我们求值,当然并不是不可以,只不过说会比较麻烦。就拿四则运算来说我们既要考虑“括号”又要考虑运算符的“优先级”。当然对于括号问题和优先级问题我们借助递归和栈都能够解决:

public int solve (String s) {// write code heres = s.trim();Deque<Integer> stack = new ArrayDeque<>();// 初始化字符、符号int number = 0;char sign = '+';char[] charArray = s.toCharArray();for (int i = 0; i < charArray.length; i ++) {// 获取当前字符char c = s.charAt(i);if (c == ' ') {// 空字符是无效字符直接跳过continue;}if (Character.isDigit(c)) {// 遇到数字时继续遍历求这个完整的数字的值,保存到 number 中(如“10”)number = number * 10 + c - '0';}if (c == '(') {// 遇到左括号时递归求这个括号里面的表达式的值// 先遍历找到对应的右括号,因为可能里面还嵌有多对括号,// 使用一个变量 counterPartition 统计括号对数直到变量为 0int j = i + 1;int counterPartition = 1;while (counterPartition > 0) {if (charArray[j] == '(') {counterPartition++;}if (charArray[j] == ')') {counterPartition--;}j++;}number = solve(s.substring(i + 1, j - 1));i = j - 1;}if (!Character.isDigit(c) || i == charArray.length) {// 遇到符号字符时,包括遍历到末尾时都需要:// 1.根据上一个运算符并把计算结果 push 进栈// 2.然后保存新的运算符到 signif (sign == '+') {stack.push(number);} else if (sign == '-') {stack.push(-1 * number);} else if (sign == '*') {stack.push(stack.pop() * number);} else if (sign == '/') {stack.push(stack.pop() / number);}number = 0;sign = c;}}// 用栈保存各部分计算的和,最后把栈中的结果求和即可int ans = 0;while (!stack.isEmpty()) {ans += stack.pop();}return ans;
}

虽然我们上面使用栈也可以解决算术(中缀)表达式求值的问题,但是整个过程肉眼可见的麻烦,那么能不能更简单有效的处理表达式求值呢?逆波兰表达式应运而生。

二、中缀表达式转逆波兰表达式

逆波兰表达式是一种不需要括号的后缀表达法,它的特点就是所有的符号都是在要运算数的后面出现。比如:"9+(3-1)*3+10/2" 转换成逆波兰表达式就成了:“9 3 1- 3*+ 10 2 /+”。显然,这里没有括号,对于习惯了算术(中缀)表达式的我们来说,这样的表述是很难受的。不过我们不喜欢,有机器喜欢,比如我们聪明的计算机。

在具体介绍如何使用逆波兰表达式求值之前,你肯定跟我一样好奇:算术表达式和逆波兰表达式之间是如何转换的?下面我们就来进行解密。

1、转换规则

从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号(乘除优先加减)则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。

2、代码实现:

    // 中缀表达式转逆波兰表达式// 定义运算符优先级private static final Map<Character, Integer> precedence = new HashMap<>();static {precedence.put('+', 1);precedence.put('-', 1);precedence.put('*', 2);precedence.put('/', 2);precedence.put('^', 3);}public static String infixToRPN(String infix) {StringBuilder output = new StringBuilder(); // 存放输出结果Stack<Character> stack = new Stack<>(); // 运算符栈for (char c : infix.toCharArray()) {if (Character.isDigit(c)) { // 如果是数字,直接输出output.append(c);} else if (c == '(') { // 如果是左括号,入栈stack.push(c);} else if (c == ')') { // 如果是右括号,将左括号之前的运算符全部输出while (!stack.isEmpty() && stack.peek() != '(') {output.append(stack.pop());}if (!stack.isEmpty()) {stack.pop(); // 弹出左括号}} else { // 如果是运算符while (!stack.isEmpty() && stack.peek() != '(' && precedence.get(c) <= precedence.get(stack.peek())) {output.append(stack.pop()); // 将优先级高于等于当前运算符的运算符全部输出}stack.push(c);}}while (!stack.isEmpty()) { // 将剩余的运算符全部输出output.append(stack.pop());}return output.toString();}

三、逆波兰表达式求值

1、求值规则

规则:从左到右遍历表达式的每个数字和符号,遇到是数字就进栈,遇到是符号,就将处于栈顶两个数字出栈,进行运算,运算结果进栈, 直到最终获得结果。

2、代码实现

public int evalRPN(String[] tokens) {Stack<Integer> stack = new Stack<>();for (String x:tokens) {if (!x.equals("+") && !x.equals("-") && !x.equals("*") && !x.equals("/")) {stack.push(Integer.parseInt(x));} else {int num1 = stack.pop();int num2 = stack.pop();switch (x) {case "+":stack.push(num2+num1);break;case "-":stack.push(num2-num1);break;case "*":stack.push(num2*num1);break;case "/":stack.push(num2/num1);break;}}}return stack.pop();}

四、拓展思考

有前缀表达式吗?当然是有的,前缀表达式就是所谓的波兰表达式,它的转换与求值和后缀表达式不同:后缀表达式的转换和运求值扫描顺序都是从左往右,而前缀表达式的转换和求值是从右向左,其他规则一致。

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

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

相关文章

Spring Boot 笔记 025 主界面

1.1 路由搭建 1.1.1 安装vue router npm install vue-router4 1.1.2 在src/router/index.js中创建路由器&#xff0c;并导出 import { createRouter, createWebHistory } from vue-router//导入组件 import LoginVue from /views/Login.vue import LayoutVue from /views/La…

cmake 项目。qt5升级 qt6 报错 error: “Qt requires a C++17 compiler 已解决

日常项目开发中。需要对qt5升级到qt6 做cmake兼容配置&#xff0c;在编译中发现&#xff0c;有c 编译环境 报错 2>C:\Qt\6.5.3\msvc2019_64\include\QtCore/qcompilerdetection.h(1226,1): fatal error C1189: #error: "Qt requires a C17 compiler, and a suitable …

JavaSec 基础之 XXE

文章目录 XMLReaderSAXReaderSAXBuilderDocumentBuilderUnmarshaller**SAXParserFactory**XMLReaderFactoryDigester总结 XMLReader public String XMLReader(RequestBody String content) {try {XMLReader xmlReader XMLReaderFactory.createXMLReader();// 修复&#xff1a…

Spring之AOP源码解析(下)

前言 在上一遍文章中,我们主要讲解了ProxyFactory在Spring完成AOP动态代理的过程中发挥的作用。这一篇我们主要讲解这些注解都是如何注入Advisors,然后分析这些Advisors生效的条件 注解都是如何注入Advisor并匹配的 EnableTransactionManagement注解 我们在之前提到EnableT…

【Java程序设计】【C00290】基于Springboot的网上书城管理系统(有论文)

基于Springboot的网上书城管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的网上书城管理系统 本系统分为系统功能模块、管理员功能模块以及用户功能模块。 系统功能模块&#xff1a;在系统首页可以查看首…

Day07-面向对象-封装课后练习以及参考答案

文章目录 巩固题包练习成员变量基础题第1题&#xff1a;员工第2题&#xff1a;日期类 成员方法基础题第3题&#xff1a;三角形第4题&#xff1a;日期类第5题&#xff1a;数组工具类 拔高题第6题&#xff1a;公民类第7题&#xff1a;数组工具类 巩固题 包练习 每道作业题可以单…

12 个顶级音频转换器软件(免费)

当涉及不受支持的音乐文件时&#xff0c;音频文件转换器软件总是会派上用场。当您希望缩小大量大型音乐文件的大小以节省设备存储空间时&#xff0c;它也很有帮助。您在寻找传输音频的软件吗&#xff1f;好吧&#xff0c;请仔细选择音频转换器&#xff0c;因为最好的音乐转换器…

C语言数据存储

目录 一.数据类型的介绍 &#xff08;1&#xff09;整形家族 &#xff08;2&#xff09;浮点型家族 &#xff08;3&#xff09;构造类型 &#xff08;4&#xff09;其他 二.整形在内存中如何进行存储 &#xff08;1&#xff09;原&#xff0c;反&#xff0c;补 &#xf…

《Docker 简易速速上手小册》第1章 Docker 基础入门(2024 最新版)

文章目录 1.1 Docker 简介与历史1.1.1 Docker 基础知识1.1.2 重点案例&#xff1a;Python Web 应用的 Docker 化1.1.3 拓展案例 1&#xff1a;使用 Docker 进行 Python 数据分析1.1.4 拓展案例 2&#xff1a;Docker 中的 Python 机器学习环境 1.2 安装与配置 Docker1.2.1 重点基…

k8s-hpa控制器 16

hpa可通过metrics-server所提供pod的cpu或者内存的负载情况&#xff0c;从而动态拉伸控制器的副本数&#xff0c;从而达到后端的自动弹缩 官网&#xff1a;https://kubernetes.io/zh/docs/tasks/run-application/horizontal-pod-autoscalewalkthrough/ 上传镜像 创建hpa实例 …

Java语言实现学生成绩排序问题

目录 题目&#xff1a; 代码展示&#xff1a; Student类 TestStudent类 运行结果 ​编辑 题目&#xff1a; 给定一段字符串,里面包含若干个学生上机和笔试成绩如 String str "张三:上机成绩90,笔试成绩78; 李四:上机成绩68,笔试成绩98; …

React18源码: reconcliler启动过程

Reconcliler启动过程 Reconcliler启动过程实际就是React的启动过程位于react-dom包&#xff0c;衔接reconciler运作流程中的输入步骤.在调用入口函数之前&#xff0c;reactElement(<App/>) 和 DOM对象 div#root 之间没有关联&#xff0c;用图片表示如下&#xff1a; 在启…

反序列化字符串逃逸 [安洵杯 2019]easy_serialize_php1

打开题目 $_SESSION是访客与整个网站交互过程中一直存在的公有变量 然后看extract()函数的功能&#xff1a; extract($_POST)就是将post的内容作为这个函数的参数。 extract() 函数从数组中将变量导入到当前的符号表(本题的作用是将_SESSION的两个函数变为post传参) function…

C++ //练习 8.9 使用你为8.1.2节(第281页)第一个练习所编写的函数打印一个istringstream对象的内容。

C Primer&#xff08;第5版&#xff09; 练习 8.9 练习 8.9 使用你为8.1.2节&#xff08;第281页&#xff09;第一个练习所编写的函数打印一个istringstream对象的内容。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /*****…

爬虫知识--03

数据存mysql import requests from bs4 import BeautifulSoup import pymysql# 链接数据库pymysql conn pymysql.connect(userroot,password"JIAJIA",host127.0.0.1,databasecnblogs,port3306, ) cursor conn.cursor() cursor conn.cursor()# 爬数据 res request…

ChatGPT Plus遇到订阅被拒原因与解决方案

ChatGPT Plus被广泛认为相比普通版本更快、更强&#xff0c;并且能最先体验新功能。 很多小伙伴再订阅时遇到图片中的问题 错误提示包括这些&#xff1a; Your credit card was declined.Try paying with a debit card instead.您的信用卡被拒绝了。请尝试用借记卡支付。你的…

Android 圆环带刻度条进度动画效果实现

效果图 需求是根据传感器做一个重力球效果&#xff0c;先实现了动画后续加上跟传感器联动. 又是摆烂的一天&#xff0c; 尚能呼吸&#xff0c;未来可期啊 View源码 package com.android.circlescalebar.view;import android.content.Context; import android.content.res.Typ…

【Crypto | CTF】BugKu 简单的RSA

天命&#xff1a;这题也不算简单了&#xff0c;要反编译&#xff0c;要灵活一点 首先收到pyc文件&#xff0c;拿去反编译出来&#xff0c;可以用在线反编译&#xff0c;也可以用工具反编译 在线&#xff1a;python反编译 - 在线工具 工具&#xff1a;https://download.csdn.n…

RocketMQ快速实战以及集群架构原理详解

RocketMQ快速实战以及集群架构原理详解 组成部分 启动Rocket服务之前要先启动NameServer NameServer 提供轻量级Broker路由服务&#xff0c;主要是提供服务注册 Broker 实际处理消息存储、转发等服务的核心组件 Producer 消息生产者集群&#xff0c;通常为业务系统中的一个功…

华清远见作业第四十一天——Qt(第三天)

思维导图&#xff1a; 编程 完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如…