【算法】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 )

101

程序:

 1 package com.beyond.algs4.experiment;
 2
 3 import com.beyond.algs4.lib.Stack;
 4 import com.beyond.algs4.std.StdIn;
 5 import com.beyond.algs4.std.StdOut;
 6
 7 public class Evaluate {
 8
 9     public static void main(String[] args) {
10         Stack<String> ops = new Stack<String>();
11         Stack<Double> vals = new Stack<Double>();
12         // 读取字符,如果是运算符则压入栈
13         while (!StdIn.isEmpty()) {
14             String s = StdIn.readString();
15             if         (s.equals("("))                ;
16             else if (s.equals("+"))        ops.push(s);
17             else if (s.equals("-"))        ops.push(s);
18             else if (s.equals("*"))        ops.push(s);
19             else if (s.equals("/"))        ops.push(s);
20             else if (s.equals("sqrt"))    ops.push(s);
21             else if (s.equals(")")) {
22                 // 如果符号为“)”, 弹出运算符和操作数,计算结果并压入栈
23                 String op = ops.pop();
24                 double v = vals.pop();
25                 if        (op.equals("+"))    v = vals.pop() + v;
26                 else if (op.equals("-"))    v = vals.pop() - v;
27                 else if (op.equals("*"))    v = vals.pop() * v;
28                 else if (op.equals("/"))    v = vals.pop() / v;
29                 else if (op.equals("sqrt"))    v = Math.sqrt(v);
30                 vals.push(v);
31             }
32             else vals.push(Double.parseDouble(s));
33             StdOut.println("操作数栈:" + vals.toString());
34             StdOut.println("运算符栈:" + ops.toString());
35         }
36         StdOut.println(vals.pop());
37     }
38
39 }

输出:

( 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

时间: 2024-10-12 05:04:51

【算法】E.W.Dijkstra算术表达式求值的相关文章

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

这两天看到的内容是关于栈和队列,在栈的模块发现了Dijkstra双栈算术表达式求值算法,可以用来实现计算器类型的app. 编程语言系统一般都内置了对算术表达式的处理,但是他们是如何在内部实现的呢?为了了解这个过程,我们可以自行搭建一套简易的算术表达式处理机制,这里就用到栈特性和本篇提到的Dijkstra算法. 概述:     算术表达式可能是一个数.或者是由一个左括号.一个算术表达式.一个运算符.另一个算术表达式和一个右括号组成的表达式.为了简化问题,这里定义的是未省略括号的算术表达式,它明确地

Dijkstra的双栈算术表达式求值

import java.util.Stack; import java.util.Scanner; public class Evaluate { public static void main(String[] args) { Stack<String> ops=new Stack<String>(); Stack<Double> vals=new Stack<Double>(); Scanner cin=new Scanner(System.in); /

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

表达式由括号, 运算符和操作数(数字)组成.我们根据以下4中情况从左到右逐个将这些实体送入栈处理. (1)将操作数压入操作数栈: (2)将运算符压入运算符栈: (3)忽略左括号: (4)在遇到右括号时, 弹出一个运算符,弹出所需数量的操作符,并将运算符和操作符的运算结果压入操作数栈. [地杰斯特拉的双栈算术表达式求值算法] public class Evaluate { public static void main(String[] args) { Stack<String> ops = n

栈的应用—算术表达式求值

例三.算术表达式求值 1.问题描述 当一个算术表达式中含有多个运算符,且运算符的优先级不同的情况下,如何才能处理一个算术表达式????? 2.思路 首先我们要知道表达式分为三类:  ①中缀表达式:a+(b-c/d)*e ②前缀表达式+a*-be ③后缀表达式abcd/-e*+ 由于运算符有优先级,所以在计算机中计算一个中缀的表达式非常困难,特别是带括号的更麻烦,而后缀表达式中既无运算符优先又无括号的约束问题因为在后缀表达式中运算符出现的顺序正是计算的顺序,所以计算一个后缀的表达式更简单.所以,可

数据结构 -- 整数算术表达式求值 (C/C++)

数据结构题集(C语言版)--严蔚敏,吴伟民编著 设置运算符栈和运算数栈辅助分析运算符有限关系. 读入表达式的字符序列的同时完成运算符和运算数(整数)的识别处理,以及相应的运算. 在识别出运算数的同时,要将其字符序列形式转换成整数形式. 1 /** 2 Function:整数算术表达式求值 3 Date:2014-11-10 4 Author:JackDawson 5 Compiler:gcc version 4.8.1 6 */ 7 #include <iostream> 8 #include

简单算术表达式求值

#include <stdio.h> #include <string.h> int main() { int n,i; char a[200]; int f=0,l=0; gets(a); for(i=0;i<strlen(a);i++) { if(a[i]>='0'&&a[i]<='9')//如果是数字 { if(a[i+1]>='0'&&a[i+1]<='9')//如果后面那个也是数字 { f=(f+a[i]-48

java实现算术表达式求值

需要根据配置的表达式(例如:5+12*(3+5)/7.0)计算出相应的结果,因此使用java中的栈利用后缀表达式的方式实现该工具类. 后缀表达式就是将操作符放在操作数的后面展示的方式,例如:3+2 后缀表达式为32+,3*(2+1)的后缀表达式为:321+*,解决表达式求值首先需要根据字符串表达式求出后缀表达式,然后使用后缀表达式和操作数栈实现计算,计算的大致思想是从后缀表达式中取元素,如果元素是数值则加入到操作数栈中,如果是运算符则从操作数栈中取两个数来参与运算.后缀表达式的获取要借助于两个栈

桟的使用之算术表达式求值

1.  背景知识 对于算术表达式(1+((2+3)*(4+5))),如何使用程序进行计算能够很好表示运算符的优先级,从而获得正确的结果呢?简化处理,我们将算术表达式当做一个字符串,包含运算数,左括号,运算符和右括号,这里只处理带有加减乘除以及求平方根的运算.E.W.Dijkstra发明了一种简单的算法,使用两个栈,一个用于保存运算符,一个用于保存运算数,具体算法操作如下: 表达式由括号,运算符和操作数组成,依据以下4中情况从左到右将这些实体送入桟中 将操作数送入操作数桟: 将运算符送入运算符桟:

Openjudge-计算概论(A)-简单算术表达式求值

描述: 两位正整数的简单算术运算(只考虑整数运算),算术运算为: +,加法运算:-,减法运算:*,乘法运算:/,整除运算:%,取余运算. 算术表达式的格式为(运算符前后可能有空格):运算数 运算符 运算数 请输出相应的结果. 输入一行算术表达式.输出整型算数运算的结果(结果值不一定为2位数,可能多于2位或少于2位). 样例输入 32+64 样例输出 96思路:使用字符串,从前往后扫,直至结束,把符号提出来,进行运算.注意:输入时一定要用gets(char)库函数,否则会报错的!代码如下: 1 #