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 ...
随机推荐
- POJ 2069 模拟退火算法
Super Star Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6422 Accepted: 1591 Spec ...
- Elasticsearch入门CRUD(新增、查询、修改、删除)
1. 项目中引用 Elasticsearch.NET Elasticsearch 其他版本可在: http://www.nuget.org/ 找到对应的项目以源码 ! 本文以 2.4 ...
- java使用Redis2--保存对象
Redis中并没有提供set(String key, Object obj)的方法,但提供了set(final byte[] key, final byte[] value) 的方法,可以通过把对象转 ...
- [Objective-C语言教程]多态(26)
多态性这个词表示有许多形式. 通常,当存在类的层次结构并且通过继承相关时,会发生多态性. Objective-C多态表示对成员函数的调用将导致执行不同的函数,具体取决于调用该函数的对象的类型. 考虑下 ...
- [iOS笔试600题]二、常识篇(共有72题)
[B]1.NSObject是一个根类,几乎所有的类都是从它派生而来.但是根类并不拥有真它类都有的alloc和init方法?[判断题] A. 正确 B. 错误 [A]2. UIResponder可以让继 ...
- Security-OAuth2.0 密码模式之客户端实现
我的OAuth2.0 客户端项目目录 pom 的配置 <?xml version="1.0" encoding="UTF-8"?> <proj ...
- SAE实践——创建简单留言板
1. 创建数据库 将mysaeapptest的数据库初始化之后,建立新的数据库,在PHPMyAdmin输入下列SQL. CREATE TABLE IF NOT EXISTS `ny_diary` ( ...
- c++之随堂笔记
1.指针篇 给指针赋值时,只能等号右边只能使用&符号将一个对象的地址赋值给指针,不能直接把一个具体的数或者字符串直接赋值给指针. 举例: int* ptr_num = 100; //这种写法 ...
- 丢用lamp手动安装apache php mysql
Centos7环境下. 使用lamp环境无法正常显示出thinkphp站点的内容,一气之下,选择手动安装 第一步: 安装apache php 和php连接数据库的工具php-mysql [root@ ...
- SpringDataRedis java.net.UnknownHostException: 127.0.0.1 错误
找了半天发现原来配置文件中多了一个空格; idea中properties文件的127.0.0.1后面出现了一个空格,编辑器将其变深黄色了