算术表达式求值

我们要学习的一个栈的用例同时也是展示泛型的应用的一个经典例子,就是用来计算算术表达式的值,例如

( 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算术表达式求值的更多相关文章

  1. Dijkstra的双栈算术表达式求值算法

    这次来复习一下Dijkstra的双栈算术表达式求值算法,其实这就是一个计算器的实现,但是这里用到了不一样的算法,同时复习了栈. 主体思想就是将每次输入的字符和数字分别存储在两个栈中.每遇到一个单次结束 ...

  2. 算法手记(2)Dijkstra双栈算术表达式求值算法

    这两天看到的内容是关于栈和队列,在栈的模块发现了Dijkstra双栈算术表达式求值算法,可以用来实现计算器类型的app. 编程语言系统一般都内置了对算术表达式的处理,但是他们是如何在内部实现的呢?为了 ...

  3. 栈的一个实例——Dijkstra的双栈算术表达式求值法

    Dijkstra的双栈算术表达式求值法,即是计算算术表达式的值,如表达式(1 + ( (2+3) * (4*5) ) ). 该方法是 使用两个栈分别存储算术表达式的运算符与操作数 忽略左括号 遇到右括 ...

  4. page80-栈用例-算术表达式求值

    表达式由括号, 运算符和操作数(数字)组成.我们根据以下4中情况从左到右逐个将这些实体送入栈处理. (1)将操作数压入操作数栈: (2)将运算符压入运算符栈: (3)忽略左括号: (4)在遇到右括号时 ...

  5. 利用栈实现算术表达式求值(Java语言描述)

    利用栈实现算术表达式求值(Java语言描述) 算术表达式求值是栈的典型应用,自己写栈,实现Java栈算术表达式求值,涉及栈,编译原理方面的知识.声明:部分代码参考自茫茫大海的专栏. 链栈的实现: pa ...

  6. OpenJudge计算概论-简单算术表达式求值

    /*===================================== 简单算术表达式求值 总时间限制: 1000ms 内存限制: 65536kB 描述 2位正整数的简单算术运算(只考虑整数运 ...

  7. [Java]算术表达式求值之三(中序表达式转二叉树方案 支持小数)

    Entry类 这个类对表达式的合法性进行了粗筛: package com.hy; import java.io.BufferedReader; import java.io.IOException; ...

  8. [Java]算术表达式求值之二(中序表达式转后序表达式方案,支持小数)

    Inlet类,入口类,这个类的主要用途是验证用户输入的算术表达式: package com.hy; import java.io.BufferedReader; import java.io.IOEx ...

  9. java实现算术表达式求值

    需要根据配置的表达式(例如:5+12*(3+5)/7.0)计算出相应的结果,因此使用java中的栈利用后缀表达式的方式实现该工具类. 后缀表达式就是将操作符放在操作数的后面展示的方式,例如:3+2 后 ...

随机推荐

  1. C# 程序开始主要是写类和方法 的基本步骤和调用方法

    主程序的使用方式以及调用方法字段.属性.方法 using System; using System.Collections.Generic; using System.Linq; using Syst ...

  2. 每天进步一点--c#基础巩固,事件、委托

    要想技术有所提高,就是把有些问题真正的弄懂弄明白,我从事C#开发两年了,一直对事件委托等概念一知半解,有时候博客园上看看别的大牛的文章,看看懂了就过去了,时间长了又忘了,真正理解还是要自己动手弄些例子 ...

  3. Python判断是否是数字(无法判断浮点数)(已解决)

    s为字符串s.isalnum() 所有字符都是数字或者字母s.isalpha() 所有字符都是字母s.isdigit() 所有字符都是数字s.islower() 所有字符都是小写s.isupper() ...

  4. DB2数据库之间联邦

    现在有以下两个数据库:sample,QIN 需要在数据库QIN中访问sample中的表ACT 1.数据库编目 C:\Users\QIN>db2 catalog tcpip node OLIVER ...

  5. QTP获取系统时间并自定义格式

    function GetDateTime(Nowstr)          Dim Currentdatetime           Dim YY   'Year          Dim MM   ...

  6. How to Notify Command to evaluate in mvvmlight

    How to Raize Command to evalituate in mvvm In mvvmlight, we bind our control to the relaycommand obj ...

  7. Jquery 1.8.2 click function - 动态

    Jquery 动态按钮方法如下:   $("#parentID").on("click",'.classname',function(){$(this).par ...

  8. 元音字母A的发音规则

    摘抄自百度文库 A/a的发音比较复杂,归纳起来有10种情况: 一.在重读开音节中读[ei]. 例如: plane [plein]  radio [ˈreidiəu] wake [weik]  pape ...

  9. Matlab实现均匀量化

    Matlab实现均匀量化 首先读入一个音频文件的前200个点,如果音频通道大于1则只取一个通道,滤掉其余的 得到音频文件的最大值和最小值,最大值和最小值的差除以2的4次方即16得到量化电平的端点间隔. ...

  10. 给Eclipse提速的7个技巧(转)

    本文由 ImportNew - 孙 波翔 翻译自 nicolasbize.欢迎加入翻译小组.转载请参见文章末尾的要求. 大约一个月前,我发表了一篇博客,其中介绍了对Eclipse的爱与恨. 有些人问我 ...