波兰式、逆波兰式是《数据结构》课程中讲解关于栈的时候提到的,栈是很简单的一种数据结构。但是这些理论的提出却是计算机早期发展领域的重大突破,值得仔细回味。

1. 中缀表达式

我们在数学中学到的表达式被称为中缀表达式,操作符号在操作数中间,比如 2 + 3 * (5 - 1)。对人类而言,这种表达方式显而易见,求值也很直接,先算乘除再算加减,先算括号内再算括号外。

然而,这个表达式对于计算机而言却很费解。你可能会有疑问:这有什么难理解的嘛,在JavaScript、Python或者Ruby,甚至是Java里面都可以通过eval("2 + 3 * (5 - 1)")来计算这个表达式。当然,这里的计算机并不是指现而今强大的计算机和高级编程语言,而是指上个世纪中页还处于发展初期的计算机。

2. 前缀表达式

早在1920年,波兰科学家扬·武卡谢维奇就发明了一种不需要括号的表示法,可以用来表示一个计算表达式。即将操作符号写在操作数之前,也就是前缀表达式,即波兰式(Polish Notation, PN)。这种表达式直到1960年计算机出现后才发挥出其威力。

比如2 + 3 * (5 - 1)这个表达式的前缀表达式为+ 2 * 3 - 5 1来表示。

阅读这个表达式需要从左至右读入表达式,如果一个操作符后面跟着两个操作数时,则计算,然后将结果作为操作数替换这个操作符和两个操作数,重复此步骤,直至所有操作符处理完毕。从左往右依次读取,直到遇到- 5 1,做计算后,将表达式替换为+ 2 * 3 4,然后从左往右再次读取,直到遇到* 3 4,做计算后将表达式替换为+ 2 12,然后从左往右依次读取,读到+ 2 12,计算得到14,到此结束。

可以看到,这种计算过程也相当复杂,需要多次遍历表达式,而且需要识别一个操作符后面跟着两个操作数这种模式,相比而言,下文中的逆波兰式要更为直接和简单。

如果你熟悉各种编程语言的话,这很像Lisp语言中的表达式(如下代码)。需要注意的是,Lisp语言中的括号并不是数学意义上的的括号,Lisp中的函数是可以携带多个参数的,比如(+ 1 2 3),因此需要使用括号来标明函数参数。

Clojure1.5.1user=>(+2(*3(-51)))14

3. 后缀表达式

后缀表达式也称为逆波兰式(Reverse Polish Notation, RPN),更加广为人知一些,和前缀表达式刚好相反,是将操作符号放置于操作数之后,比如2 + 3 * (5 - 1)用逆波兰式来表示则是:2 3 5 1 - * +。

逆波兰式的计算也是从左往右依次读取,当读到操作符时,将之前的两个操作数做计算,然后替换这两个操作数和操作符,接着读取,重复此步骤。对于这个表达式,读到5 1 -,得到4,然后读取乘号,取出前面的3和上一步的计算结果4,并计算,到12,接着读取加号+,计算2 12 +得到14,计算结束。

上面这个步骤可以很容易的用栈来实现:

从左往右依次读取表达式,如果是数字则将该数字压栈,如果是符号,则将之前的两个数字出栈,做计算后,将计算结果压栈,直到表达式读取结束。栈中剩下的一个数就是计算结果。

逆波兰式看起来像波兰式反过来,比如5 + 1的波兰式是+ 5 1,逆波兰式为5 1 +或者1 5 +。也很明显,逆波兰式并不是简单的将波兰式反过来,因为,减法和除法中减数和被减数、除数与被除数是不能交换的,即- 10 5和- 5 10就完全不一样。

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

因为通过后缀表达式来进行计算只需要一个栈即可,从硬件和软件上实现都是极为便利的,因此逆波兰式在计算机领域的应用更加广泛,因此将中缀表达式转换为逆波兰式非常重要。

依然仅仅使用栈就可以将中缀表达式转换成逆波兰式,转换过程如下:

从左往右遍历中缀表达式中的每个数字和符号,弱是数字就输出,成为逆波兰式的一部分; 如果是右括号,或者是其他符号并且比当前栈顶符号的优先级低,则栈顶元素依次出栈并输出; 然后将当前符号进栈,重复以上操作直到结束。

还是以2 + 3 * (5 - 1)为例:

首先读入数字2,直接将其输出,输出为2,栈为空

接着读入加号+,由于栈为空,因此将其进栈,输出为2,栈为+

接着读入数字3,直接将其输出,输出为2 3,栈为+

接着读入乘号*,比栈顶元素优先级高,进栈,输出为2 3,栈为+ *

读入左括号(,直接进栈,输出2 3,栈为+ * (

读入数字5,直接将其输出,输出为2 3 5,栈为+ * (

读入减号-,栈顶元素为左括号,进栈,输出为2 3 5,栈为+ * ( -

读入数字1,直接将其输出,输出为2 3 5 1,栈为+ * ( -

读入右括号,依次输出栈顶元素,直到左括号,括号不输出,输出2 3 5 1 -,栈为+ *

已经无元素可读,依次输出栈顶元素,直到栈为空,输出2 3 5 1 - * +,栈为空

这样可以仅仅使用栈,首先将中缀表达式转换为逆波兰式,然后用本文第3节中的方法对后缀表达式进行求值,整个过程使用栈来完成即可。

5. 表达式树与逆波兰式

还可以通过另外一种方法来将一个表达式转换成波兰式和逆波兰式,这种方法依赖与树,首先需要根据表达式构建成树,仍然以2 + 3 * (5 - 1)为例,下图是其表达式树。

我们发现这个树的后序遍历结果为2 3 5 1 - * +,刚好是其逆波兰式;而其先序遍历结果为+ 2 * 3 - 5 1刚好为其波兰式;中序遍历就不用说了,就是我们常见的中缀表达式。我们也可以通过这种特性来实现表达式的各种表示方法的转换。

6. 参考

java逆波兰式求值_波兰式、逆波兰式与表达式求值相关推荐

  1. 信息学奥赛一本通 1397:简单算术表达式求值 | OpenJudge NOI 1.12 01:简单算术表达式求值

    [题目链接] ybt 1397:简单算术表达式求值 OpenJudge NOI 1.12 01:简单算术表达式求值 [题目考点] 1. 函数 2. 选择结构 [解题思路] 这一章节都是练习函数,那么这 ...

  2. 已知三角形三点坐标求角度_高中数学:椭圆相关角度的最值问题

    圆锥曲线中的最值问题主要包括长度最值.角度最值及面积最值等. 例题:如图1,已知椭圆的中心在坐标原点,焦点在x轴上,长轴 的长为4,左准线与x轴的交点为M, . (1)求椭圆的方程: (2)若直线 , ...

  3. android log 如何获取double类型后小数点的值_【ES6基础】Symbol介绍:独一无二的值...

    开篇 ES6之前我们都清楚JS有六种数据类型:Undefined.Null.布尔值(Boolean).字符串(String).数值(Number).对象(Object),今天笔者讲的Symbol类型是 ...

  4. 二元函数对xy同时求导_让向量、矩阵和张量的求导更简洁些吧

    本文是我在阅读Erik Learned-Miller的<Vector, Matrix, and Tensor Derivatives>时的记录,点此下载. 本文的主要内容是帮助你学习如何进 ...

  5. 已知坐标求方位角_【干货】RTK视频实操 | 求转换参数详解和七点注意事项!

    RTK视频教学,『求转换参数』,技术员结合测量一线实操经验,运用工程之星5.0软件为大家讲解如何求转换参数,以及求转换参数需要注意哪些事项. 求转换参数操作 通常情况下,科力达工程之星直接输出的坐标为 ...

  6. java接口参数默认值_下面关于setMaxAge(int expires)方法参数默认值的描述中,正确的是(5.0分)_学小易找答案...

    [单选题]下面选项中,用于在web.xml中配置监听器的元素是(5.0分) [填空题]Cookie技术用于将会话过程中的数据保存到( )中,从而使浏览器和服务器可以更好地进行数据交互.(5.0分) [ ...

  7. python惰性求值_让Python中类的属性具有惰性求值的能力

    起步 我们希望将一个只读的属性定义为 property 属性方法,只有在访问它时才进行计算,但是,又希望把计算出的值缓存起来,不要每次访问它时都重新计算. 解决方案 定义一个惰性属性最有效的方法就是利 ...

  8. java响应式网页设计_基于HTML5的响应式网站的设计与实现(论文).docx

    毕业论文 基于HTML5的响应式网站的设计与实现 摘 要 随着网络的发展和普及,各类建站技术的更新与运用,现在搭建一个网站的时间和成本越来越低,使得企业官方网站得到了极大的发展.从早期简单的网页展示, ...

  9. java 三边求面积_已知三角形的三边长如何求面积?

    展开全部 各类三角形求面积方式如下所示: 1.已知e69da5e6ba9062616964757a686964616f31333365666264三角形底a,高h,则 S=ah/2 2.已知三角形三边 ...

  10. java局部网内通话杂音_在Spring Boot反应式Web应用程序上启用SSL,并在控制台中对该打印进行http调用时出现异常噪音...

    在我创建了我的spring boot反应式Web应用程序以支持SSL之后,当我尝试对服务器进行http调用时,它会在控制台中的异常跟踪下面打印 . 作为应用程序所有者,我无法阻止任何人使用我的服务 . ...

最新文章

  1. linux scp移动文件夹,linux scp远程拷贝文件及文件夹
  2. linux 多线程的基础 交通信号灯学习笔记 :信号详解
  3. rpm包安装和卸载,rpm查询,yum工具详解,yum仓库搭建
  4. Jmeter基础(二)
  5. 案例4-1.6 树种统计 (25 分)_18行代码AC
  6. java第二章复习_JAVA第二章知识点
  7. fatal error C1083:无法打开包括文件:“stdint.h”: No such file or directory解决方案
  8. 一步步实现SDDC-NSX MGR安装和主机准备
  9. 关于jquery中prev()和next()的用法
  10. Appscan的下载安装
  11. Android 计步器 - 手机自带系统级的 健康运动App 授权
  12. 成功三大定律:荷花定律、金蝉定律、竹子定律
  13. 简单的三点式腰背肌锻炼方法
  14. 网络电话VOIP技术解析
  15. 巨潮资讯网上市公司股票讯息批量下载
  16. 外部多端口映射Https443端口配置
  17. python生成泊松分布随机数_Python-Numpy-Poisson分布
  18. Gartner:VPT技术原理 ——如何确定网络攻击面上的风险优先级
  19. 用 Python 找出了拉黑 QQ 空间屏蔽我的大人物
  20. 低代码助力生产管理:车间管理系统

热门文章

  1. Python-opencv读取视频流处理后保存成mp4格式的视频源码
  2. win7 重装系统变 win10
  3. 道德与企业成败 【如何搞垮一个企业.序】
  4. 前端页面生成神器以及后端变量命名神器
  5. 机器人的弊议论文_辩证对待机器人-议论文范文600字
  6. JETT(一)-Excel模板转换器简介
  7. 自定义 showToast 组件,可直接使用,附源代码和使用说明
  8. 这种公司再也呆不下去了!我要跳槽!
  9. 这三年,一路走来,劈荆斩棘 – Vol 2
  10. 「 LaTex 」使用多级标题