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 ...
随机推荐
- mysql 赋给用户远程权限 grant all privileges on
我配置了权限 就可以在Windows下访问我虚拟机中的数据库了 来源:http://blog.csdn.net/louisliaoxh/article/details/52767209 登录: 在本机 ...
- Linux下安装pip(遇到了python2.6升级为python2.7道路上的坑,原因已经找到,只差临门一脚了,以后补上)
1.先说一下什么是pippip 是“A tool for installing and managing Python packages.”,也就是说pip是python的软件安装工具2.下面介绍怎么 ...
- apache2 的https配置和代理https后端nodejs配置
先进入 /usr/local/apache2/conf 目录 修改 vim httpd.conf 把下面的去掉注释 LoadModule proxy_module modules/mod_pro ...
- SQLServer 的存储过程与java交互
一. 存储过程简介 Sql Server的存储过程是一个被命名的存储在服务器上的Transacation-Sql语句集合,是封装重复性工作的一种方法,它支持用户声明的变量.条件执行和其他强大的编程 ...
- P4842 城市旅行
题目链接 题意分析 首先存在树上的删边连边操作 所以我们使用\(LCT\)维护 然后考虑怎么维护答案 可以发现 对于一条链 我们编号为\(1,2,3,...,n\) 那么期望就是 \[\frac{a_ ...
- sqlplus登录远程数据库与数据导出
一.登录 1.cmd中输入sqlplus /nolog 2.链接数据库,root是用户名,root123是密码,ORCL是数据库名.conn root/root123@192.168.1.27:152 ...
- L06-Ubuntu系统中部署Vagrant和VirtualBox
一.前言 1.Vagrant是一个搭建完整的虚拟开发环境的工具~~~更多关于Vagrant理论可查看这篇博文https://www.cnblogs.com/davenkin/p/vagrant-vir ...
- linux系统服务管理
centos7的服务管理命令 systemctl start 服务名称 systemctl stop 服务名称 systemctl status 服务名称 systemctl restart 服务名称 ...
- 阿里云 下载的 apache 证书 转换为 pcks8 证书
第一步: 百度 搜索 rsa 转 pcks8 将 .key 文件 转换成 pcks8.key . 第二部: 将 chain.crt 的 内容 复制到 public.crt 下方.. 新的 publi ...
- 数学还勉强管用,用代码还能画个canvas 仪表盘(含完整代码)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...