相关介绍:

 该算法用于求得一个字符串形式的表达式的结果。例如,计算1+1+(3-1)*3-(21-20)/2所得的表达式的值,该算法利用了两个栈来计算表达式的值,为此,称为双栈法,其实现简单且易于理解。但其要求将我们平时所看到的表达式的模式转化为完全加括号的形式。如表达式,1+1+(3-1)*3-(21-20)/2是我们平时习惯上的样子,其完全加括号的形式为,(((1+1)+((3+1)*2))-((21-20)/2))。由此可知,计算一个字符串形式的表达式有两个任务,第一是将输入的算术表达式转化为一个完全加括号的算术表达式,第二是将一个完全加括号的算术表达式进行运算,求得运算的结果。为方便起见,这里只考虑加减乘除(+、-、×、÷)这四个基本运算

将算术表达式转化为完全加括号:

其基本思想如下:

  1. 初始化一个运算符栈和一个操作数栈
  2. 从算术表达式输入的字符串中从左到右的读取一个字符
  3. 若当前字符为操作数,则直接将该操作数压入操作数栈中
  4. 若当前字符是左括号"("时,将其压入运算符栈中
  5. 若当前字符为运算符时,则:
    1. 当运算符栈为空的时候,则将其压入运算符栈中
    2. 当此运算符的优先级高于栈顶元素的运算符的时候,则将此运算符压入操作数栈中,否则,弹出运算符栈顶元素和操作数栈顶的两个元素,为其添加上相应的运算符以及左括号和右括号之后,将其压入操作数栈中,将其看成一个整体
  6. 若当前字符为右括号")"时,反复将栈顶元素弹出,每次弹出一个运算符的时候,从操作数栈中弹出栈顶的两个元素为其添加上相应的运算符以及左右括号之后,再将其压入操作数栈中,将其看成一个整体。直至运算符栈的栈顶元素为左括号为止,再将左括号出栈并丢弃。
  7. 若读取还未完毕,重复步骤2
  8. 若读取完毕,则将栈中剩余的所有运算符依次弹出,每次弹出一个运算符时,同时弹出操作数栈的两个元素,并为其添加上相应的运算符以及左右括号之后,将其作为一个整体,压入操作数栈中。直到运算符栈为空为止,则操作数栈中的结果,即为所得的结果。

运算符的优先级如下表所示:

运算符 +(加)、-(减) *(乘)、/(除)
优先级 1 2

其中,其数值越大的表示其运算符的优先级越高。

示例代码如下:

package queueandstack;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
/**
* 该类用于演示使用双栈法求解算术表达式的结果
* @author 学徒
*
*/
public class DoubleStackGetResult
{ public static void main(String[] args)
{
DoubleStackGetResult ds=new DoubleStackGetResult();
System.out.println(ds.ComplementBrackets("1+1+(3+1)*2-(21-20)/2"));
}
//该方法用于实现一个符号表
private static Map<String,Integer> getMap()
{
Map<String,Integer> temp=new HashMap<String,Integer>();
//定义各个运算符的优先级,其中,x和÷字符用于兼容
temp.put("*", 2);
temp.put("/", 2);
temp.put("×",2);
temp.put("÷",2);
temp.put("+", 1);
temp.put("-",1);
return temp;
}
/**
* 该方法用于将输入的,习惯上的算术表达式转化为完全加括号的形式
* @param input 输入的习惯上的算术表达式
*/
//该符号表用于定义运算符的优先级
private Map<String,Integer> table=getMap();
//该字符串为用于匹配数字的
private String rexNumber="\\d+";
private String rexOperator="[((+\\-*/×÷))]";
//操作数栈
Stack<String> number=new Stack<String>();
//运算符栈
Stack<String> save=new Stack<String>();
/**
* 该方法用于将中序表达式转化为后序表达式,并对其转化后的表达式以字符串的形式进行返回
* @return 后序表达式
*/
public String ComplementBrackets(String input)
{
//用于获得字符串中的数字所组成的数组
String[] numbers=input.split(rexOperator);
//用于指示是获取了第几个数字数组中的数字整体
int order=0;
//用于指示当前字符的指针
int i=0;
while(i<input.length())
{
//获得当前字符
String thisString=String.valueOf(input.charAt(i));
//当前该字符为运算符或者括号时,即当前该字符不为数字时
if(thisString.matches(rexOperator))
{
//当当前字符不为左括号或者右括号时(即为运算符)
if(!thisString.matches("[()()]"))
{
//用于记录栈顶元素的优先级
int temporary=0;
//获取当前字符的优先级
int present=table.get(thisString);
//当操作数的栈不为空的时候
if(!save.isEmpty())
{
//查看栈顶元素的字符以及其优先级
String top=save.peek();
if(!top.matches("[((]"))
{
temporary=table.get(top);
}
}
//当栈顶元素的操作符的优先级比当前操作符的优先级还要高或者相同时,对其进行弹出操作,直到栈顶元素的优先级比当前操作符的优先级要低
if(temporary>=present)
{
while(!save.isEmpty()&&table.get(save.peek())>=present)
{
String number1=number.pop();
String number2=number.pop();
number.push("("+number2+save.pop()+number1+")");
}
}
save.push(thisString);
}
//当当前的字符为左括号的时候,直接将其压入栈中
else if(thisString.matches("[((]"))
{
save.push(thisString);
}
//当当前的字符为右括号的时候,将其栈中的元素一直弹出,直至遇到左括号结束,并将左括号弹出
else
{
while(!save.peek().matches("[((]"))
{
String number1=number.pop();
String number2=number.pop();
number.push("("+number2+save.pop()+number1+")");
}
//弹出其左括号
save.pop();
/*String number1=number.pop();
String number2=number.pop();
number.push("("+number2+save.pop()+number1+")");*/
}
i++;
}
//当前该字符为数字的时候
if(thisString.matches(rexNumber))
{
//用于存储数字数组中的数字字符串
String numberString=null;
do
{
numberString=numbers[order];
//当数字字符串中的数字不为空时(由于可能会是空字符串的出现),将整个中序表达式的字符串的指针进行向右移动
if(!numberString.trim().equals(""))
{
i+=numberString.length();
order++;
break;
}
else
{
order++;
}
}while(true);
//将数字直接压入操作数栈中
number.push(numberString);
}
}
//将栈中剩余的字符进行弹出
while(!save.isEmpty())
{
String number1=number.pop();
String number2=number.pop();
number.push("("+number2+save.pop()+number1+")");
}
return number.pop();
}
} 运行结果如下:
(((1+1)+((3+1)*2))-((21-20)/2))

计算完全加括号表达式的结果:

其基本思想如下:

  1. 初始化两个栈,一个用于保存运算符,一个用于保存操作数
  2. 从左往右依次遍历字符串表达式的每个字符
  3. 将操作数压入操作数栈中
  4. 将运算符压入运算符栈中
  5. 忽略左括号
  6. 在遇到右括号时,弹出一个运算符以及所需数量的操作数,并将运算符和操作数的运行结果压入到操作数栈中
  7. 处理完最后一个右括号,操作数栈上只会有一个值,它就是表达式的值

这种方法不难理解,每当算法遇到一个被括号包围并由一个运算符和两个操作数组成的子表达式时,它都能够将运算符和操作数的计算结果压入操作数栈中,这样的结果就是在输入中用这个运算所得的值代替了该子表达式,因此,用这个值代替子表达式得到的结果和原表达式相同,通过反复运用以上的规律,最终可以得到该表达式的解。

其示例代码如下:

/**
*
* 用于计算一个完全加括号的算术表达式的结果
* @param inputStr 其参数为完全加括号的算术表达式
*
*/
public double getResult(String inputStr)
{
//用于将输入的字符串分割成数字的正则表达式
String regex="[((+\\-*/×÷))]";
//用于获取得到数字
String[] numbers =inputStr.split(regex);
int order=0;
//两个栈,一个为操作数栈,一个为运算符栈
Stack<String> ops=new Stack<String>();
Stack<Double> vals=new Stack<Double>();
char[] input=inputStr.toCharArray();
//用于遍历的字符的指针
int index=0;
while(index<input.length)
{
//读取对应的字符
char ch=input[index];
//忽略左括号
if(ch=='('||ch=='(');
else if(ch=='+')
ops.push(String.valueOf(ch));
else if(ch=='-'||ch=='-')
ops.push(String.valueOf(ch));
else if(ch=='*'||ch=='×')
ops.push(String.valueOf(ch));
else if(ch=='/'||ch=='÷')
ops.push(String.valueOf(ch));
else if(ch==')'||ch==')')
{
//当为右括号的时候,弹出运算符以及操作数,计算结果并压入栈中
String op=ops.pop();
double v=vals.pop();
if(op.equals("+"))
v=vals.pop()+v;
else if(op.equals("-")||op.equals("-"))
v=vals.pop()-v;
else if(op.equals("*")||op.equals("×"))
v=vals.pop()*v;
else if(op.equals("/")||op.equals("÷"))
v=vals.pop()/v;
vals.push(v);
}
else
{
//用于存储数字数组中的数字字符串
String numberString=null;
do
{
numberString=numbers[order];
//当数字字符串中的数字不为空时(由于可能会是空字符串的出现),将整个中序表达式的字符串的指针进行向右移动
if(!numberString.trim().equals(""))
{
index+=numberString.length()-1;
order++;
break;
}
else
{
order++;
}
}while(true);
vals.push(Double.parseDouble(numberString));
}
++index;
}
return vals.pop();
}

回到目录|·(工)·)

K:双栈法求算术表达式的值的更多相关文章

  1. PTA笔记 堆栈模拟队列+求前缀表达式的值

    基础实验 3-2.5 堆栈模拟队列 (25 分) 设已知有两个堆栈S1和S2,请用这两个堆栈模拟出一个队列Q. 所谓用堆栈模拟队列,实际上就是通过调用堆栈的下列操作函数: int IsFull(Sta ...

  2. pat02-线性结构3. 求前缀表达式的值(25)

    02-线性结构3. 求前缀表达式的值(25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 算术表达式有前缀表示法.中缀表示法和后缀表示法 ...

  3. 信息竞赛进阶指南--递归法求中缀表达式的值,O(n^2)(模板)

    // 递归法求中缀表达式的值,O(n^2) int calc(int l, int r) { // 寻找未被任何括号包含的最后一个加减号 for (int i = r, j = 0; i >= ...

  4. openjduge 求简单表达式的值

    表达式求值 总时间限制:  10000ms  单个测试点时间限制:  1000ms  内存限制:  131072kB 给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值. 输入 输入仅有一行 ...

  5. 【Zhejiang University PATest】02-3. 求前缀表达式的值

    算术表达式有前缀表示法.中缀表示法和后缀表示法等形式.前缀表达式指二元运算符位于两个运算数之前,例如2+3*(7-4)+8/4的前缀表达式是:+ + 2 * 3 - 7 4 / 8 4.请设计程序计算 ...

  6. 3-07. 求前缀表达式的值(25) (ZJU_PAT数学)

    题目链接:http://pat.zju.edu.cn/contests/ds/3-07 算术表达式有前缀表示法.中缀表示法和后缀表示法等形式.前缀表达式指二元运算符位于两个运算数之前,比如2+3*(7 ...

  7. 重温C语言(1)----计算算术表达式的值

    <C程序设计语言>练习题 5-10 编写程序 expr,计算从命令行输入的逆波兰表达式的值,其中每个运算符或操作数用一个单独的参数表示.例如,命令 expr 2 3 4 + * 计算表达式 ...

  8. [LeetCode] Evaluate Division 求除法表达式的值

    Equations are given in the format A / B = k, where A and B are variables represented as strings, and ...

  9. [LeetCode] 399. Evaluate Division 求除法表达式的值

    Equations are given in the format A / B = k, where A and B are variables represented as strings, and ...

随机推荐

  1. (四)SSO之CAS框架单点登录,自定义验证登录方式

    应需求的变化,在登录cas的时候,默认根据用户名和密码进行验证,如果加上用户名,密码和一个系统标识进行验证呢?该如何做呢? 我们知道cas默认的登录界面中,输入的用户名和密码,再配置一下deploye ...

  2. php性能优化三(PHP语言本身)

    0.用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的“函数”(译注:PHP手册 ...

  3. navicat for oracle 创建表ID字段的自动递增

    Oracle数据库创建表ID字段的自动递增   将表t_uaer的字段ID设置为自增:(用序列sequence的方法来实现) ----创建表 Create table t_user( Id numbe ...

  4. IIFE格式js写法

    创建 加载文件方法 输出方法 方法书写 输出自定义变量 完整写法

  5. JavaScript执行环境和作用域(链)的那些事

    执行环境 什么是执行环境 提起作用域,我们不得不说说什么是执行环境.执行环境定义了变量或函数有权访问的其他数据,并决定其各自的行为.每一个执行环境都有一个对应的变量对象,这个对象的作用就是保存在环境中 ...

  6. 编程开发之--java多线程学习总结(3)类锁

    2.使用方法同步 package com.lfy.ThreadsSynchronize; /** * 1.使用同步方法 * 语法:即用 synchronized 关键字修饰方法(注意是在1个对象中用锁 ...

  7. 10. JavaScript学习笔记——JSON

    10. JSON ///[JSON是一种数据格式,不是JS 独有的] ///[JSON语法] /* 1.数据书写格式:"name":value,JSON要求给属性名加上[双引号], ...

  8. testng XMl 参数化

    方法一: 方法二: 方法三: (1)如果测试的数据较多的情况下,很显然这种方式不适合,那么可以通过@DataProvider生成测试数据,通过@Test(dataProvider = "&q ...

  9. Mac下使用Eclipse的Show in Terminal提示command not found: mvn

    在Mac下一般配置了Maven的环境变了一般都不会提示,但是如果使用zsh的扩展之后,系统默认的环境变量配置文件会发生变化,尤其使用Eclipse打开终端时,默认不会去读取用户目录下的~/.bashr ...

  10. DB2 Check Pending Script

    转载 http://www.zinox.com/archives/144 Thanks to Max Petrenko of DB2 Toronto Lab for sharing a very us ...