【算法】E.W.Dijkstra算术表达式求值
算术表达式求值
我们要学习的一个栈的用例同时也是展示泛型的应用的一个经典例子,就是用来计算算术表达式的值,例如
( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )
如果将4乘以5,把3加上2,取它们的积然后加上1,就得到了101。但Java系统是如何完成这些运算的呢?不需要研究Java系统的构造细节,我们也可以编写一个Java程序来解决这个问题。它接受一个输入字符串(表达式)并输出表达式的值。为了简化问题,首先来看一下这份明确的递归定义:算术表达式可能是一个数,或者是由一个左括号、一个算术表达式、一个运算符、另一个算术表达式和一个右括号组成的表达式。简单起见,这里定义的是未省略括号的算术表达式,它明确地说明了所有运算符的操作数——你可能更熟悉形如 1 + 2 * 3 的表达式,省略了括号,而采用优先级规则。我们将要学习的简单机制也能处理优先级规则,但在这里我们不想把问题复杂化。为了突出重点,我们支持最常见的二元运算符*、+、-和/,以及只接受一个参数的平方根运算符sqrt。我们也可以轻易支持更多数量和种类的运算符来计算多种大家熟悉的数学表达式,包括三角函数、指数和对数函数。我们的重点在于如何解释由括号、运算符和数字组成的字符串,并按照正确的顺序完成各种初级算术运算操作。如何才能够得到一个(由字符串表示的)算术表达式的值呢?E.W.Dijkstra在20世纪60年代发明了一个非常简单的算法,用两个栈(一个用于保存运算符,一个用于保存操作数)完成了这个任务,其实现过程如下,运行轨迹如输出结果。
表达式由括号、运算符和操作数(数字)组成。我们根据以下4种情况从左到右逐个将这些实体送入栈处理:
- 将操作数压入操作数栈
- 将运算符压入运算符栈
- 忽略左括号
- 在遇到有括号时,弹出一个运算符,弹出所需数量的操作数,并将运算符和操作数的运算结果压入操作数栈
在处理完最后一个右括号之后,操作数栈上只会有一个值,它就是表达式的值。这种方法乍一看有些难以理解,但要证明它能够计算得到正确的值很简单:每当算法遇到一个被括号包围并由一个运算符和两个操作数组成的子表达式时,它都将运算符和操作数的计算结果压入操作数栈。这样的结果就像在输入中用这个值代替了该子表达式,因此用这个值代替子表达式得到的结果和原表达式相同。我们可以反复应用这个规律并得到一个最终值。例如,用该算法计算以下表达式得到的结果都是相同的
( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )
( 1 + ( ( 5 ) * ( 4 * 5 ) ) )
( 1 + ( 5 * 20 ) )
( 1 + 100 )
程序:
- package com.beyond.algs4.experiment;
- import com.beyond.algs4.lib.Stack;
- import com.beyond.algs4.std.StdIn;
- import com.beyond.algs4.std.StdOut;
- public class Evaluate {
- public static void main(String[] args) {
- Stack<String> ops = new Stack<String>();
- Stack<Double> vals = new Stack<Double>();
- // 读取字符,如果是运算符则压入栈
- while (!StdIn.isEmpty()) {
- String s = StdIn.readString();
- if (s.equals("(")) ;
- else if (s.equals("+")) ops.push(s);
- else if (s.equals("-")) ops.push(s);
- else if (s.equals("*")) ops.push(s);
- else if (s.equals("/")) ops.push(s);
- else if (s.equals("sqrt")) ops.push(s);
- else if (s.equals(")")) {
- // 如果符号为“)”, 弹出运算符和操作数,计算结果并压入栈
- String op = ops.pop();
- double v = vals.pop();
- if (op.equals("+")) v = vals.pop() + v;
- else if (op.equals("-")) v = vals.pop() - v;
- else if (op.equals("*")) v = vals.pop() * v;
- else if (op.equals("/")) v = vals.pop() / v;
- else if (op.equals("sqrt")) v = Math.sqrt(v);
- vals.push(v);
- }
- else vals.push(Double.parseDouble(s));
- StdOut.println("操作数栈:" + vals.toString());
- StdOut.println("运算符栈:" + ops.toString());
- }
- StdOut.println(vals.pop());
- }
- }
输出:
- ( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )
- 操作数栈:
- 运算符栈:
- 操作数栈:1.0
- 运算符栈:
- 操作数栈:1.0
- 运算符栈:+
- 操作数栈:1.0
- 运算符栈:+
- 操作数栈:1.0
- 运算符栈:+
- 操作数栈:2.0 1.0
- 运算符栈:+
- 操作数栈:2.0 1.0
- 运算符栈:+ +
- 操作数栈:3.0 2.0 1.0
- 运算符栈:+ +
- 操作数栈:5.0 1.0
- 运算符栈:+
- 操作数栈:5.0 1.0
- 运算符栈:* +
- 操作数栈:5.0 1.0
- 运算符栈:* +
- 操作数栈:4.0 5.0 1.0
- 运算符栈:* +
- 操作数栈:4.0 5.0 1.0
- 运算符栈:* * +
- 操作数栈:5.0 4.0 5.0 1.0
- 运算符栈:* * +
- 操作数栈:20.0 5.0 1.0
- 运算符栈:* +
- 操作数栈:100.0 1.0
- 运算符栈:+
- 操作数栈:101.0
- 运算符栈:
参考资料:
算法 第四版 谢路云 译 Algorithms Fourth Edition [美] Robert Sedgewick, Kevin Wayne著
http://algs4.cs.princeton.edu/home/
源码下载链接:
http://pan.baidu.com/s/1c0Ao7Bi
【算法】E.W.Dijkstra算术表达式求值的更多相关文章
- Dijkstra的双栈算术表达式求值算法
这次来复习一下Dijkstra的双栈算术表达式求值算法,其实这就是一个计算器的实现,但是这里用到了不一样的算法,同时复习了栈. 主体思想就是将每次输入的字符和数字分别存储在两个栈中.每遇到一个单次结束 ...
- 算法手记(2)Dijkstra双栈算术表达式求值算法
这两天看到的内容是关于栈和队列,在栈的模块发现了Dijkstra双栈算术表达式求值算法,可以用来实现计算器类型的app. 编程语言系统一般都内置了对算术表达式的处理,但是他们是如何在内部实现的呢?为了 ...
- 栈的一个实例——Dijkstra的双栈算术表达式求值法
Dijkstra的双栈算术表达式求值法,即是计算算术表达式的值,如表达式(1 + ( (2+3) * (4*5) ) ). 该方法是 使用两个栈分别存储算术表达式的运算符与操作数 忽略左括号 遇到右括 ...
- page80-栈用例-算术表达式求值
表达式由括号, 运算符和操作数(数字)组成.我们根据以下4中情况从左到右逐个将这些实体送入栈处理. (1)将操作数压入操作数栈: (2)将运算符压入运算符栈: (3)忽略左括号: (4)在遇到右括号时 ...
- 利用栈实现算术表达式求值(Java语言描述)
利用栈实现算术表达式求值(Java语言描述) 算术表达式求值是栈的典型应用,自己写栈,实现Java栈算术表达式求值,涉及栈,编译原理方面的知识.声明:部分代码参考自茫茫大海的专栏. 链栈的实现: pa ...
- OpenJudge计算概论-简单算术表达式求值
/*===================================== 简单算术表达式求值 总时间限制: 1000ms 内存限制: 65536kB 描述 2位正整数的简单算术运算(只考虑整数运 ...
- [Java]算术表达式求值之三(中序表达式转二叉树方案 支持小数)
Entry类 这个类对表达式的合法性进行了粗筛: package com.hy; import java.io.BufferedReader; import java.io.IOException; ...
- [Java]算术表达式求值之二(中序表达式转后序表达式方案,支持小数)
Inlet类,入口类,这个类的主要用途是验证用户输入的算术表达式: package com.hy; import java.io.BufferedReader; import java.io.IOEx ...
- java实现算术表达式求值
需要根据配置的表达式(例如:5+12*(3+5)/7.0)计算出相应的结果,因此使用java中的栈利用后缀表达式的方式实现该工具类. 后缀表达式就是将操作符放在操作数的后面展示的方式,例如:3+2 后 ...
随机推荐
- VM虚拟机无法拖拽、粘贴、复制
VM无法从客户机拖放/复制文件到虚拟机的解决办法: 将这两项取消勾选,点击[确定].再次打开,勾选,点击[确定] 原因分析:可能是VM中默认是不支持该功能的,但是在配置窗体上确实默认打钩打上的. 依据 ...
- 共享内存shared pool (5):详解一条SQL在library cache中解析
前面介绍的 shared pool,library cache结构,都是为了说明一条SQL是如何被解析的.先看下面的图: 图中涉及的各结构简单介绍 父HANDLE,里面有父游标堆0的地址.. 父游标堆 ...
- 第十四章 调试及安全性(In .net4.5) 之 对称及非对称加密
1. 概述 本章内容包括:对称及非对称加密算法..net中的加密类.使用哈希操作.创建和管理签名认证.代码访问权限 和 加密字符串. 2. 主要内容 2.1 使用对称和非对称加密 ① 对称加密:使用同 ...
- rails 学习笔记
bundle package #保存gem到 vendor/cache bundle install –local 从cache从安装 升级rails bundle config –delete ...
- Ruby处理二进制(未完成)
https://practicingruby.com/articles/binary-file-formats http://stackoverflow.com/questions/16821435/ ...
- Windows上搭建android开发环境
在搭建android开发环境时需要四部分内容,框架如下 其中Java SDK和Eclipse在java4android中有过介绍,重点介绍ADT和Android SDK的安装. 安装Android S ...
- C 构造一个 简单配置文件读取库
前言 最近看到这篇文章, json引擎性能对比报告 http://www.oschina.net/news/61942/cpp-json-compare?utm_source=tuicool 感觉技术 ...
- Shell 内置操作符-字符串处理(汇总)
一.判断读取字符串值 表达式 含义 ${var} 变量var的值, 与$var相同 ${var-DEFAULT} 如果var没有被声明, 那么就以$DEFAULT作为其值 * ${var:-D ...
- 编译原理之lex,yacc学习
写在前面的几句废话 最近在项目的过程中接触了lex 和 yacc,他们可以帮助我们来实现自己的领域语言.最典型的应用就是可以帮助我们来实现自定义测试脚本的执行器.但是,这里也有一个限制,就是测试脚本要 ...
- CoverFlow效果
1. 成员函数 mCamera是用来做类3D效果处理,比如z轴方向上的平移,绕y轴的旋转等 mMaxRotationAngle是图片绕y轴最大旋转角度,也就是屏幕最边上那两张图片的旋转角度 mMaxZ ...