首页 > 其他分享 >利用栈实现四则运算表达式求值----先将中缀表达式转换成后缀表达式,然后再求后缀表达式的值

利用栈实现四则运算表达式求值----先将中缀表达式转换成后缀表达式,然后再求后缀表达式的值

时间:2023-06-01 12:35:27浏览次数:38  
标签:opt 出栈 中缀 后缀 括号 表达式 入栈

利用栈实现四则运算表达式求值,附Python代码

中缀表达式和后缀表达式

平时用到的标准的四则运算表达式就叫做中缀表达式,例如“9 +(3 - 1) * 3 + 10 / 2)”,特点是运算符在数字中间;

后缀表达式就是一种把运算符放在数字后面的形式,“9 3 1 - 3 * + 10 2 / +”即为上例中缀表达式对应的后缀表达式形式,后缀表达式还有一个特点就是消除了所有的括号;

中缀表达式能够非常直观地展示出运算关系,很方便手动计算,但是如果要设计一个计算机程序来计算这个表达式却变得非常繁琐,不仅要考虑四则运算的优先级,还要考虑括号的影响,而后缀表达式虽然在表现形式上不直观却非常便于计算机进行计算。

后缀表达式计算结果

后缀表达式的计算要借助栈来实现。

规则:从左到右遍历表达式的每个数字和符号,遇到的是数字就进栈,遇到的时符号就将栈顶的两个数字出栈进行计算,然后将计算结果入栈,最终栈里的值即为计算的结果。

以中缀表达式“9 +(3 - 1) * 3 + 10 / 2)”的后缀表达式“9 3 1 - 3 * + 10 2 / +”为例,计算过程如下:

1、初始化一个空栈

2、后缀表达式前三个都是数字,所以9、3、1依次入栈,此时栈内元素为(最左边为栈底元素):9 3 1

3、接下来为减号“-”,此时1出栈作为第二个数,3出栈作为第一个数(因为栈内元素是先进后出,所以最先出栈的为运算中的第二个数,接着出栈的才是运算中的第一个数),进行进行减法运算3 - 1 = 2然后入栈,此时栈内元素为:9 2

4、接下来为数字3,进栈,栈内元素为:9 2 3

5、接下来为乘号“*”,此时3、2出栈,进行乘法运算2 * 3 = 6,结果入栈,此时栈内元素为:9 6

6、接下来是加号“+”,9、6出栈进行加法运算,结果15入栈,此时栈内元素为:15

7、接下来是数字10、2入栈,此时栈内元素为:15 10 2

8、接下来是除号“/”,10、2出栈进行除法运算,结果5入栈,此时栈内元素为:15 5

9、最后一个是加号“+”,15、5出栈进行加法运算,结果20即为整个表达式的运算结果,与中缀表达式计算结果一致

只需要按顺序遍历就能够计算出结果,不用考虑对四则运算法则做复杂的逻辑处理,对计算机处理来说是非常方便的,这样一来,最重要的问题就是怎样将中缀表达式转化为后缀表达式。

中缀表达式转化为后缀表达式

中缀表达式转化为后缀表达式同样要借助栈来实现,不同于中缀表达式的计算,这里的栈用于存贮运算符号而不是数值。

规则:从左到右遍历中缀表达式中的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号则要分为两种情况:

1)是括号时,如果是左括号,直接将左括号入栈,如果是右括号则栈顶元素依次出栈并输出,直到有一个左括号出栈(出栈的左括号不输出到后缀表达式)。

2)是运算符号时,如果栈顶符号为左括号,则直接将这个运算符号入栈。栈顶符号不为左括号时,如果该运算符号优先级比栈顶运算符号高则入栈,比栈顶符号低或者相等时则栈顶元素依次出栈并输出直到栈为空或者栈顶为左括号为止,然后将这个符号入栈。

最后将栈顶符号依次出栈并输出,得到的结果即为最终的后缀表达式。

同样以中缀表达式“9 +(3 - 1) * 3 + 10 / 2)”为例推导其后缀表达式的步骤如下:

1、初始化一个空栈

2、第一个数字9直接输出到后缀表达式,第二个符号“+”入栈,此时后缀表达式:9    栈内符号:+

3、接下里左括号入栈,数字3输出,此时后缀表达式为: 9 3    栈内元素为:+ (

4、接下来是减号“-”,由于栈顶为左括号,直接入栈,数字1输出,此时后缀表达式:9 3 1    栈内符号:+ ( -

5、接下来是右括号,栈顶符号“-”出栈并输出,接着栈顶左括号出栈(括号匹配的处理),此时后缀表达式:9 3 1 -   栈内符号:+

6、接下来是乘号“*”,比栈顶符号加号“+”优先级高(在出stack的过程中去处理优先级,优先级高的后缀表达式靠前),入栈,数字3输出,此时后缀表达式:9 3 1 - 3    栈内符号:+ *

7、接下来是加号“+”,比栈顶“*”优先级低,栈顶“*”出栈并输出,接着与新的栈顶加号“+”比较优先级相同(贪心思路求最高优先级的),栈顶“+”出栈并输出,栈为空,到此为止,然后将加号“+”入栈。此时后缀表达式为:9 3 1 - 3 * +    栈内符号:+

8、接下来数字10输出,除号“/”优先级比栈顶加号“+”优先级高直接入栈,此时后缀表达式:9 3 1 - 3 * + 10   栈内为:+ /

9、最后数字2输出,遍历结束后,栈顶符号依次出栈并输出,得到最终的后缀表达式:9 3 1 - 3 * + 10 2 / +

优化与整合

通过前面的介绍,只需要经过将中缀表达式转化为后缀表达式,再计算后缀表达式这两步就能得到一个四则运算表达式的值。这两步中各用到了一个栈,推导后缀表达式时用到的栈存储的是运算符号以及括号(只有左括号,没有右括号),计算后缀表达式时用到的栈存储的是数字,两个步骤分开先后执行要遍历一次中缀表达式载遍历一遍后缀表达式。同时在中缀表达式推导后缀表达式以及计算后缀表达式的过程中,都是要对字符串进行操作,如果将两步合并同时执行的话,不仅能够简化代码,更能提高运算效率,因为这样全程只需要遍历一次中缀表达式就可以完成计算。

思路:用一个栈data保存运算数字,一个栈opt保存运算符号。从左到右遍历中缀表达式,如果是数字就入栈data,如果是符号,以下四种情况直接将符号入栈opt:1)栈为空;2)栈顶为左括号;3)该符号为左括号;4)该运算符号优先级比栈顶符号高。如果是右括号,则执行一次计算步骤:从opt出栈一个运算符号,从data出栈两个数字进行一次运算并将结果入栈data。重复执行该计算步骤,直到opt栈顶为左括号,然后将该左括号出栈;如果该符号优先级低于opt栈顶符号或者与栈顶符号优先级相同时,重复执行与之前相同的计算步骤,直到opt栈为空,若中途opt栈顶符号为左括号则停止执行计算步骤。中缀表达式遍历完成后,继续执行之前的计算步骤直到opt栈为空。

因为Python处理字符串比较简洁方便,代码使用Python3编写,为输入方便起见,其中输入的表达式字符串中不包含空格,代码中忽略了对输入表达式不合法的处理,假设输入表达式是可以正确计算的:

def compare(op1, op2):
    """
    比较两个运算符的优先级,乘除运算优先级比加减高
    op1优先级比op2高返回True,否则返回False
    """
    return op1 in ["*", "/"] and op2 in ["+", "-"]
 
 
def getvalue(num1, num2, operator):
    """
    根据运算符号operator计算结果并返回
    """
    if operator == "+":
        return num1 + num2
    elif operator == "-":
        return num1 - num2
    elif operator == "*":
        return num1 * num2
    else:  # /
        return num1 / num2
 
 
def process(data, opt):
    """
    opt出栈一个运算符,data出栈两个数值,进行一次计算,并将结果入栈data
    """
    operator = opt.pop()
    num2 = data.pop()
    num1 = data.pop()
    data.append(getvalue(num1, num2, operator))
 
 
def calculate(s):
    """
    计算字符串表达式的值,字符串中不包含空格
    """
    data = []  # 数据栈
    opt = []  # 操作符栈
    i = 0  # 表达式遍历索引
    while i < len(s):
        if s[i].isdigit():  # 数字,入栈data
            start = i  # 数字字符开始位置
            while i + 1 < len(s) and s[i + 1].isdigit():
                i += 1
            data.append(int(s[start: i + 1]))  # i为最后一个数字字符的位置
        elif s[i] == ")":  # 右括号,opt出栈同时data出栈并计算,计算结果入栈data,直到opt出栈一个左括号
            while opt[-1] != "(":
                process(data, opt)
            opt.pop()  # 出栈"("
        elif not opt or opt[-1] == "(":  # 操作符栈为空,或者操作符栈顶为左括号,操作符直接入栈opt
            opt.append(s[i])
        elif s[i] == "(" or compare(s[i], opt[-1]):  # 当前操作符为左括号或者比栈顶操作符优先级高,操作符直接入栈opt
            opt.append(s[i])
        else:  # 优先级不比栈顶操作符高时,opt出栈同时data出栈并计算,计算结果如栈data
            while opt and not compare(s[i], opt[-1]):
                if opt[-1] == "(":  # 若遇到左括号,停止计算
                    break
                process(data, opt)
            opt.append(s[i])
        i += 1  # 遍历索引后移
    while opt:
        process(data, opt)
    print(data.pop())
 
 
if __name__ == '__main__':
    exp = "(9+((3-1)*3+10/2))*2"
    calculate(exp)

  


标签:opt,出栈,中缀,后缀,括号,表达式,入栈
From: https://blog.51cto.com/u_11908275/6393204

相关文章

  • 正则表达式:书写规则
        ......
  • C++ 在 cout 中使用关系表达式
    用std::cout输出关系运算表达式时,关系表达式要加括号,否则编译会报错。例如:#include<iostream>intmain(intargc,char**argv){std::cout<<1<2<<std::endl;return0;}在linux中编译后报错内容如下:test.cpp:Infunction'intmain(int,char**)':te......
  • 认识Lambda表达式
         ......
  • HDU1403(后缀数组--最长公共子串)
    题目:LongestCommonSubstring题意:判断给定的两个串中,最长的公共串。思路:将它们合并为一个串,然后利用后缀数组求解。首先是二倍增算法:时间复杂度为O(n*log(n))#include<stdio.h>#include<string.h>#definemax1000010intwa[max],wb[max],wv[max],ws[max];intrank[max],he......
  • yulong-hids 规则引擎,目前看到就是正则表达式和count技术
    规则项目提供的默认规则太简单和宽泛了,甚至包含一些错误,比如:有些不太精确,比如:另外规则引擎的匹配算法没有做优化,规则或者事件一旦多起来,server的负载会很高有些太宽泛导致误报非常高:agent在测试机才装2天就有近6w条告警,这是无法运营的,当然,规则支持细粒度控制(开关)还是很不错的3、功......
  • python中如何使用正则表达式查询字符串
    '''Createdon2019年12月2日@author:hp''''''上一篇文章介绍了那么多关于正则表达式的用法,现在终于到了python中如何使用正则表达式了,不急,请诸君慢慢来''''''之前在讲字符串时,已经说过了字符串的格式化输出,大家没看的可以看我的上一篇文章格式化输出时,是含有模式串......
  • 中缀表达式转换为后缀表达式
    中缀表达式转换为后缀表达式所谓中缀表达式,指的是运算符处于操作数的中间(例:3*(4+2)),中缀表达式是人们常用的算术表示方法,但中缀表达式不容易被计算机解析,因为既要考虑运算符的优先级,还要考虑括号的处理。但中缀表达式仍被许多程序语言使用,因为它符合人们的普遍用法。后缀表达......
  • Elasticsearch专题精讲——API规范—— 一般表达式
    API规范——一般表达式1、格式化搜索结果 当任何请求URL加pretty=true参数时,返回的JSON都是格式化的(仅用于调试)。另一个选项是设置format=yaml,结果以更可读的yaml格式返回。2、可读输出 统计数据以适合人(例如"exists_time":"1h"或"size":"1KB")和计算机(例如......
  • [Quicker] 变量 表达式 插值
    插值($$开始)用于拼接文本,表达式($=)用于计算比较插值$$你好,{name},最后的访问路径:{lastPath},剪贴板文本为:{[cliptext]}$${词典变量["key3"]}$${词典变量.key3}$${列表变量[3]}$${列表变量.3}如果插值处理后,结果仍然以"$$"or"$="开始,则再次进行插值或......
  • java集合过滤出符合条件的List元素集合(lambda表达式)
    使用Java8中的lambda表达式过滤ModelMapmodel=newModelMap();TSmClazzTSmClazz=tSmClazzService.get(id);List<Student>students=TSmClazz.getStudents();if(flag.equals("0")){List<Student>boys......