K:双栈法求算术表达式的值
相关介绍:
该算法用于求得一个字符串形式的表达式的结果。例如,计算1+1+(3-1)*3-(21-20)/2所得的表达式的值,该算法利用了两个栈来计算表达式的值,为此,称为双栈法,其实现简单且易于理解。但其要求将我们平时所看到的表达式的模式转化为完全加括号的形式。如表达式,1+1+(3-1)*3-(21-20)/2是我们平时习惯上的样子,其完全加括号的形式为,(((1+1)+((3+1)*2))-((21-20)/2))。由此可知,计算一个字符串形式的表达式有两个任务,第一是将输入的算术表达式转化为一个完全加括号的算术表达式,第二是将一个完全加括号的算术表达式进行运算,求得运算的结果。为方便起见,这里只考虑加减乘除(+、-、×、÷)这四个基本运算
将算术表达式转化为完全加括号:
其基本思想如下:
- 初始化一个运算符栈和一个操作数栈
- 从算术表达式输入的字符串中从左到右的读取一个字符
- 若当前字符为操作数,则直接将该操作数压入操作数栈中
- 若当前字符是左括号"("时,将其压入运算符栈中
- 若当前字符为运算符时,则:
- 当运算符栈为空的时候,则将其压入运算符栈中
- 当此运算符的优先级高于栈顶元素的运算符的时候,则将此运算符压入操作数栈中,否则,弹出运算符栈顶元素和操作数栈顶的两个元素,为其添加上相应的运算符以及左括号和右括号之后,将其压入操作数栈中,将其看成一个整体
- 若当前字符为右括号")"时,反复将栈顶元素弹出,每次弹出一个运算符的时候,从操作数栈中弹出栈顶的两个元素为其添加上相应的运算符以及左右括号之后,再将其压入操作数栈中,将其看成一个整体。直至运算符栈的栈顶元素为左括号为止,再将左括号出栈并丢弃。
- 若读取还未完毕,重复步骤2
- 若读取完毕,则将栈中剩余的所有运算符依次弹出,每次弹出一个运算符时,同时弹出操作数栈的两个元素,并为其添加上相应的运算符以及左右括号之后,将其作为一个整体,压入操作数栈中。直到运算符栈为空为止,则操作数栈中的结果,即为所得的结果。
运算符的优先级如下表所示:
| 运算符 | +(加)、-(减) | *(乘)、/(除) |
|---|---|---|
| 优先级 | 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))
计算完全加括号表达式的结果:
其基本思想如下:
- 初始化两个栈,一个用于保存运算符,一个用于保存操作数
- 从左往右依次遍历字符串表达式的每个字符
- 将操作数压入操作数栈中
- 将运算符压入运算符栈中
- 忽略左括号
- 在遇到右括号时,弹出一个运算符以及所需数量的操作数,并将运算符和操作数的运行结果压入到操作数栈中
- 处理完最后一个右括号,操作数栈上只会有一个值,它就是表达式的值
这种方法不难理解,每当算法遇到一个被括号包围并由一个运算符和两个操作数组成的子表达式时,它都能够将运算符和操作数的计算结果压入操作数栈中,这样的结果就是在输入中用这个运算所得的值代替了该子表达式,因此,用这个值代替子表达式得到的结果和原表达式相同,通过反复运用以上的规律,最终可以得到该表达式的解。
其示例代码如下:
/**
*
* 用于计算一个完全加括号的算术表达式的结果
* @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:双栈法求算术表达式的值的更多相关文章
- PTA笔记 堆栈模拟队列+求前缀表达式的值
基础实验 3-2.5 堆栈模拟队列 (25 分) 设已知有两个堆栈S1和S2,请用这两个堆栈模拟出一个队列Q. 所谓用堆栈模拟队列,实际上就是通过调用堆栈的下列操作函数: int IsFull(Sta ...
- pat02-线性结构3. 求前缀表达式的值(25)
02-线性结构3. 求前缀表达式的值(25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 算术表达式有前缀表示法.中缀表示法和后缀表示法 ...
- 信息竞赛进阶指南--递归法求中缀表达式的值,O(n^2)(模板)
// 递归法求中缀表达式的值,O(n^2) int calc(int l, int r) { // 寻找未被任何括号包含的最后一个加减号 for (int i = r, j = 0; i >= ...
- openjduge 求简单表达式的值
表达式求值 总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 131072kB 给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值. 输入 输入仅有一行 ...
- 【Zhejiang University PATest】02-3. 求前缀表达式的值
算术表达式有前缀表示法.中缀表示法和后缀表示法等形式.前缀表达式指二元运算符位于两个运算数之前,例如2+3*(7-4)+8/4的前缀表达式是:+ + 2 * 3 - 7 4 / 8 4.请设计程序计算 ...
- 3-07. 求前缀表达式的值(25) (ZJU_PAT数学)
题目链接:http://pat.zju.edu.cn/contests/ds/3-07 算术表达式有前缀表示法.中缀表示法和后缀表示法等形式.前缀表达式指二元运算符位于两个运算数之前,比如2+3*(7 ...
- 重温C语言(1)----计算算术表达式的值
<C程序设计语言>练习题 5-10 编写程序 expr,计算从命令行输入的逆波兰表达式的值,其中每个运算符或操作数用一个单独的参数表示.例如,命令 expr 2 3 4 + * 计算表达式 ...
- [LeetCode] Evaluate Division 求除法表达式的值
Equations are given in the format A / B = k, where A and B are variables represented as strings, and ...
- [LeetCode] 399. Evaluate Division 求除法表达式的值
Equations are given in the format A / B = k, where A and B are variables represented as strings, and ...
随机推荐
- RecyclerView的通用适配器
本来这一个主题应该早就写了,只是项目多,属于自己的时间不多,所以现在才开动!! 前一段时间写了一篇文章,是关于ListView,GriView万能适配器,没有看过的同学,可以先看看那篇文章,然后在来学 ...
- docker微服务部署之:三,搭建Zuul微服务项目
docker微服务部署之:二.搭建文章微服务项目 一.新增demo_eureka模块,并编写代码 右键demo_parent->new->Module->Maven,选择Module ...
- [转] Maven 直接下载依赖项 artifact, dependency:get
[From]http://dnotes.wikidot.com/dependency:get-example To download an artifact into local repository ...
- [转] 手动上传jar包到远程仓库 (maven deploy)
[From] https://my.oschina.net/360yg/blog/1588899 前言:通常允许上传的远程仓库有两种:Snapshots和Releases,分别为快照版仓库和稳定版仓库 ...
- Bomb(要49)--数位dp
Bomb Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)Total Submi ...
- 【转】Python格式化字符串str.format()
原文地址:http://blog.xiayf.cn/2013/01/26/python-string-format/ 每次使用Python的格式字符串(string formatter),2.7及以上 ...
- Java 并发编程——Callable+Future+FutureTask
Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...
- java中的POJO、PO、VO分别是什么?
1.PO:persistant object 持久对象 可以看成是与数据库中的表相映射的java对象.使用Hibernate来生成PO是不错的选择. 2. VO:value object值对象. 通常 ...
- Python爬虫学习:Python内置的爬虫模块urllib库
urllib库 urllib库是Python中一个最基本的网络请求的库.它可以模拟浏览器的行为发送请求(都是这样),从而获取返回的数据 urllib.request 在Python3的urllib库当 ...
- shiro学习笔记_0400_自定义realm实现身份认证
自定义Realm实现身份认证 先来看下Realm的类继承关系: Realm接口有三个方法,最重要的是第三个方法: a) String getName():返回此realm的名字 b) boolean ...