数学计算式转为表达式树

news2024/9/23 3:26:50

        数据结构“栈”的一个用途就是:平衡符号,比如这样一个代数式:(a(b+c)*a(e*f+a*(c+d))),你能一眼看出这个式子的括号是否正确吗? 更何况还可以加入中括号([]),大括号({})。如果写一个算法来验证这个式子的话,利用栈这种数据结果是最方便的:

        构造一个空栈,读入字符串直到文件尾。如果字符是一个开放符号,则将其推入栈中,如果字符是一个封闭符号,则房栈空时报错;否则将栈元素弹出。如果弹出的符号不是对应的开放符号,则报错。在文件尾,如果栈非空则报错。

        检验括号是很简答的一个算法,这里就不是贴代码了。接下里想说的是数学计算式的计算,例如:“2+3*5“,给定这样一个简单的字符串能否得到正确的结果17,还是错误的结果25. 如果在加上括号()来改变运算优先级那么就更复杂了,例如计算“2+3*(4-3*3)+9”。

        我们在数学作业上看见的计算式是顺序计算式,或者说叫中序计算式,我们人在计算时很容易处理这种式子,但是计算机并不好处理,因为在考虑“2+3*5”的时候,当计算机遍历字符到3时,并不能立马计算“2+3”,他必须考虑3后面的符号,也就是在这个过程中需要存储中间结果。

        事实上,计算机处理这个问题时,更好的是处理后缀表达式(又叫逆波兰表达式,是一位波兰逻辑学家发明的),后缀表达式也很简单,举个例子“a+b*c”的后缀表达式为:"a b c* +", 那么后缀表达式是怎么计算的呢?


1.后缀表达式的计算

        假设我们要计算的后缀表达式为:"6 5 2 3 + 8  + 3 +*",我们且看是如何计算的:

首先构造一个空栈S,我们遍历后缀表达式,将前4个数字依次入栈:得到下图中的a图:

接下来是读入的是一个“+”,所以3和2依次弹出,并将他们的和5压入栈中得到图b,

接着数字“8”进栈,得到图c,然后是一个“*”运算符,同理弹出8和5,计算其结果后再重新入栈,得到图d,依次类推可以得到如下图结果:

如果之前的括号平衡检测代码你没问题的话,那么这个计算后缀表达式的代码你肯定也没问题。

这里先贴一下计算的代码:

        public static int GetValue(string postFix)
        {
            Stack<int> stack = new Stack<int>();
            foreach (var c in postFix)
            {
                if (char.IsDigit(c))
                {
                    stack.Push(c - '0');
                }
                else
                {
                    var b=stack.Pop();
                    var a=stack.Pop();
                    var ans = Calculate(a, b, c);
                    stack.Push(ans);    
                }
            }
            return stack.Pop();
        }

Calculate是计算函数:

        public static int Calculate(int a, int b, char op)
        {
            return op switch
            {
                '+' => a + b,
                '-' => a - b,
                '*' => a * b,
                '/' => a / b,
                _ => throw new ArgumentOutOfRangeException()
            };
        }

为了简单起见,这里只考虑了10以内的加减乘除,若果大于10,则遍历的应该是字符串。其它的就没啥好说的了。

        计算一个后缀表达式的时间是O(N),因为对输入中的每个元素的处理都是一些栈操作组成从而花费的常数时间。当我们得到一个后缀表达式之后,没有必要知道任何优先规则,这是一个明显的优点。那么问题来了,如何将一个自然表达式转为后缀表达式?

2.中缀表达式到后缀表达式的转换

        假设我们有一个中缀表达式"a+b*c+(d*e+f)",要将其转化为后缀表达式:"a b c * + d e * f + g * +",

        a+b*c+(d*e+f)*g .................................(1)

        a b c *+de*f+g*+ ...................................................(2)

        我们简单的分析一下

        首先后缀表达式的字母顺序和中缀表达式的字母顺序是一致的,这说明当我们遍历中缀时,如果用一个数组存储字母,那顺序是可以的。但是运算符的顺序缺发生了很多变化,仅看"a+b*c",加号+在前,但是转化为后缀表达式之后,+在*的后面,这说明遍历字符串时,符号的顺序不能用顺序结构存储。

        我们在做题是,心理有一个默认的假设,那就是先乘除再加减,这其实就是运算符优先级,前面说道,计算机在对后缀表达式求值的时候,是不需要考虑运算符的优先级的,计算机每遇到一个运算符就立即计算。那这有什么启发呢?启发就是:我们在转换为后缀表达式的过程中利用了优先级,将优先级这一信息存储到后缀表达式中。

        那再回到刚刚的"a+b*c",我们尝试构件一个栈用来存储运算符,首先+入栈,字母直接输出,遇到*,因为优先级高,故直接入栈,然后输出c,最后将符号依次出栈。

        还是用图来解释:

规则如下:

  • 当读到一个操作数时,立即把它放到输出中。操作符不立即输出,所以必须先存在栈中。
  • 如果遇见一个右括号,将右括号直接放入栈中
  • 如果遇见左括号,将栈中的符号依次弹出,知道遇见右括号为止
  • 如果是其它运算符号,那我们弹出栈元素直到发现优先级更低的元素为止。
  • 最后,如果读到了文件末尾,将栈元素全部弹出。

上图第三步,遇到一个+,但是栈顶目前是*,因此要先弹出*,然后发现接着是+,优先级相同,也弹出,为空后,将刚刚的+入栈。但是括号要另外处理,只有括号才能弹出括号。代码如下:

        public static string Change(string input)
        {
            Stack<char> op= new Stack<char>();
            string result = "";
            foreach(char c in input)
            {
                if(char.IsDigit(c))
                {
                    result += c;
                }
                else if(c=='(')
                {
                    op.Push(c);
                }
                else if(c== ')')
                {
                    while(op.Peek()!='(')
                    {
                        result+=op.Pop();
                    }
                    op.Pop();  //弹出’C'
                }
                else
                {
                    while(op.Count()>0)
                    {
                        var top=op.Peek();
                        if (Operations[top] <= Operations[c] && top!='(')
                        {
                            result+=op.Pop();
                        }
                        else
                        {
                            break;
                        }
                    }
                    op.Push(c);
                }
            }
            while(op.Count() > 0)
            {
                result += op.Pop();
            }

            return result;
        }

其中会比较优先级;优先级我直接定义在一个静态字典:

        public static readonly IDictionary<char, int> Operations = new Dictionary<char, int>()
        {
            ['+'] = 10,
            ['-'] = 10,
            ['*'] = 5,
            ['/'] = 5,
            ['('] = 1,  //特殊符号 特殊处理
            [')'] = 2,
        };

        到目前为止,我们就解决了前缀转后缀的问题,再结合之前的后缀计算方法,就可以得到最终结果。

事实上,如果运算符均是二元运算符,那么表达式还可以以树的形式展开。我们在后面会介绍如何将数学表达式转为表达式树。

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

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

相关文章

harmony应用签名

1. 随便搞个halloworld程序 2.生成私匙与证书请求文件 我就是key store file选择了一个文件夹&#xff0c;又不给任何提示&#xff0c;等我输入密码时才提示 填写图中内容即可&#xff0c;图中未填项可不填 点击完成即可

CPSE深圳充换电展开幕,飞凌嵌入式引领智能充储技术新潮流

9月6日&#xff0c;2023第六届深圳国际充电桩及换电站展览会&#xff08;简称&#xff1a;CPSE深圳充换电展&#xff09;在深圳会展中心&#xff08;福田&#xff09;开幕&#xff0c;飞凌嵌入式如期亮相&#xff0c;与来自全国的客户朋友及合作伙伴一同交流分享企业在智能充电…

WebDAV之π-Disk派盘 + 天悦日记

天悦日记是一款清爽简约的日记记录工具,通过天悦日记app随时随地快速写日记,更有智能数据统计分析报表,多端同步多种备份,本地备份和基于WebDAV协议的云端备份。跨平台使用,支持多设备、多平台无差别使用。天悦日记将每一天经历都清晰记录在手机,一目了然知道曾经的经历,…

StarRocks数据库FE——Catalog层

​仓外挂湖是指以 MPP 数据库为基础&#xff0c;使用可插拔架构&#xff0c;通过开放接口对接外部存储实现统一存储&#xff0c;在存储底层共享一份数据&#xff0c;计算、存储完全分离&#xff0c;实现从强管理到兼容开放存储和多引擎。实现方向为增加存储能力&#xff0c;提升…

Blender之锁定摄像机到视图方位

文章目录 当你在blender 中时&#xff0c;想要让你的摄像机跟随你的视图方位&#xff0c;以方便你的后期的制作&#xff0c;那应该怎么半&#xff1f; 先点击摄像机的图标&#xff0c;进入摄像机视图 然后按一下键盘的N 键&#xff0c;进入编辑模式&#xff0c;选择视图 最后…

【笔试强训选择题】Day34.习题(错题)解析

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;笔试强训选择题 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01;&#xff…

蓝天转债,双良转债上市价格预测

蓝天转债111017 基本信息 转债名称&#xff1a;蓝天转债&#xff0c;评级&#xff1a;AA&#xff0c;发行规模&#xff1a;8.7亿元。 正股名称&#xff1a;蓝天燃气&#xff0c;今日收盘价&#xff1a;9.74元&#xff0c;转股价格&#xff1a;10.13元。 当前转股价值 转债面值…

阿里巴巴API接口解析,实现获得商品详情

要解析阿里巴巴API接口并实现获取商品详情&#xff0c;你需要按照以下步骤进行操作&#xff1a; 了解阿里巴巴开放平台&#xff1a;访问阿里巴巴开放平台&#xff0c;并了解相关的API文档、开发者指南和规定。注册开发者账号&#xff1a;在阿里巴巴开放平台上注册一个开发者账…

【Unity编辑器扩展】| Inspector监视器面板扩展

前言【Unity编辑器扩展】| Inspector监视器面板扩展一、ContextMenu和ContextMenuItem二、Custom Editors 自定义编辑器三、Property Drawer 属性绘制器总结前言 前面我们介绍了Unity中编辑器扩展的一些基本概念及基础知识,还有编辑器扩展中用到的相关特性Attribute介绍。后面…

C高级文件相关指令

使用cut截取出Ubuntu用户的家目录&#xff0c;要求:不能使用“&#xff1a;”作为分隔 XMind

嵌入式Linux驱动开发(LCD屏幕专题)(三)

1. 硬件相关的操作 LCD驱动程序的核心就是&#xff1a; 分配fb_info设置fb_info注册fb_info硬件相关的设置 硬件相关的设置又可以分为3部分&#xff1a; 引脚设置时钟设置LCD控制器设置 2. 在设备树里指定LCD参数 framebuffer-mylcd {compatible "100ask,lcd_drv&qu…

E. Nastya and Potions

Problem - E - Codeforces 思路&#xff1a;想到用图论前驱图了&#xff0c;但是因为考虑可能有环的存在&#xff0c;但是其实题干中说明了不能通过一种或几种混合得到自己&#xff0c;所以就保证了不存在环&#xff0c;那就能用拓扑结构的性质做&#xff0c;用记忆化搜索就可以…

DHCP的interface(接口),global(全局)配置以及DHCP relay(中继),DHCP snooping,DHCP option

目录 1.DHCP的接口&#xff08;interface&#xff09;配置 2.DHCP的全局&#xff08;global&#xff09;配置 3.dhcp relay 4.dhcp snooping 5.option 这里有一个简单的拓扑图 交换机配置命令如下 The device is running!<Huawei> <Huawei>sys Enter system …

go语言基本操作---五

error接口的使用 Go语言引入了一个关于错误处理的标准模式&#xff0c;即error接口&#xff0c;它是Go语言内建的接口类型 type error interface {Error() string }package mainimport ("errors""fmt" )type Student struct {name stringid int }func …

【 XXL-JOB】 XXL-JOB任务分片

文章目录 前言xxl-job 分片广播任务的详细教程创建任务编写任务代码分片参数设置执行任务查看任务执行结果示例1示例2 总结 前言 xxl-job 是一个分布式任务调度平台&#xff0c;支持定时任务和分片任务。其中&#xff0c;分片任务可以将一个大任务拆分成多个小任务&#xff0c…

用vagrant快速创建linux虚拟机

参考B站&#xff1a;https://www.bilibili.com/video/BV1np4y1C7Yf 1、下载VirtualBox 2、下载vagrant 3、vagrant官网下载.box文件 官网&#xff1a;https://app.vagrantup.com/boxes/search 例如要下载这个centos/7 点进去&#xff0c;点击下载 下载后放到一个指定目录…

qt作业day2

//widget.cpp#include "widget.h" #include "ui_widget.h"void Widget::usr_login() {if("admin" this->edit_acc->text()){if("123456" this->edit_psd->text()){speech->say("登录成功");emit jump_sig1…

latex修改公式的默认编号

文章目录 问题描述省流出错演示没有载入amsmath包载入amsmath包 总结 问题描述 有时想自己定义公式的编号&#xff0c;不想用默认的编号(1) (2)…&#xff0c;我们应该怎么做呢&#xff1f; 只需看本文一分钟就能解决。 省流 开头载入amsmath包&#xff0c;然后在公式后面加…

【C4bp】

--- --- # 2% of all animal proteins contain FN3 repeat # Humans in protein body of 1/3 constitutes Collagen

最小可用产品MVP,投石问路

最小可用产品MVP【安志强趣讲281期】 MVP(Minimum Viable Product) 趣讲大白话&#xff1a;投是问路 **************************** 最小可用产品MVP可以是如下形式&#xff1a; 1、简陋产品 2、贵宾式MVP&#xff1a;为少量客户全力周到服务&#xff0c;获得市场反馈 3、静态页…