比windows自带计算器还强的四则复杂运算计算器!

实测随机打出两组复杂算式:-7.5 * 6 / ( -2 + ( -6.5 -  -5.22 ) )与   7.5+-3*8/(7+2)

windows的科学计算器计算结果分别为:-3.28(错误)和9(错误),全错!!!不信的小伙伴可以口算下。

正确答案是:13.71951219512195和4.833333333333334

中缀表达式转后缀表达式并进行计算的计算器(支持带负号、括号和小数的加减乘除运算)

一步一步来

假设有这样一个算式:-7.5 * 6 / ( -2 + ( -6.5 -  -5.22 ) )

我们先要将这一字符串进行分析,哪些是数字,哪些是符号。(比如:-7.5就是数字)

具体代码如下:


// 表达式转中缀表达式(支持小数点,负数,小括号)
public List<String> getList(String expression) {
List<String> exp = new ArrayList<String>();//存放解析后的中缀表达式
String merge = "";
int index = 0;
char ch, ch1 = 0;
boolean flag = false;
expression = expression.replace(" ", "");
while (index != expression.length()) {
ch = expression.substring(index, index + 1).charAt(0);
merge += ch;
if (index < expression.length() - 1) {// 防止下标越界
ch1 = expression.substring(index + 1, index + 2).charAt(0);
if (isOper(ch1)) {
exp.add(merge);
merge = "";
flag = false;
} else if (isOper(ch)) {
if (flag == false && exp.size() > 0) {
exp.add(merge);
merge = "";
flag = false;
}
}
if (ch == '(' && ch1 == '-' || isOper(ch) && ch1 == '-')
flag = true;
}
index++;
}
exp.add(merge);
return exp;
}

接下来就是重头戏了!!!中缀表达式—>后缀表达式思路分析图:


遵循以上红字规则处理第二个左括号“(”后面的元素,直到右括号“)”


将符号栈中所有符号弹出加入到集合List中,直到碰到一个与右括号匹配的左括号


以上就是从中缀表达式转换到后缀表达式的图解!得:后缀表达式为:7.5  -  6  *  2  -  6.5  -  5.22  -  -  +  /

下面是代码详解:


 // 中缀表达式转后缀表达式(有个记录负号索引的全局变量minus,方便计算)
public List<String> toRPN(String expression) {
List<String> list=getList( expression );
List<String> rPN=new ArrayList<String>();// 用于存放后缀表达式
Stack<String> exp=new Stack<String>();// 用于操作判断符号
minus=new ArrayList<>();
for (String s : list) {// 将中缀表达式每个元素list遍历
if (isOper( s )) {// 如果字符串是+-*/
if (exp.isEmpty())// 如果栈是空,那么就直接入栈
exp.push( s );//-7.5*6/(-2+(-6.5- -5.22))
else if (priority( s ) <= priority( exp.peek() )) {// 如果栈里面有运算符,那么就和栈顶运算符比较优先级
rPN.add( exp.pop() );// 当前运算符优先级小于栈顶运算符,就把栈顶运算符弹出加入到后缀表达式rPN列表中
exp.push( s );// 然后压入当前运算符
} else
exp.push( s ); } else if (s.equals( ")" )) {// 如果当前字符串是‘)’那么就将栈顶字符串加入add到List rPN中,直到栈顶字符串为"("
while (!exp.peek().equals( "(" )) {
rPN.add( exp.pop() );
}
exp.pop();// 一定要记得将左括号(弹出去,代表一对小括号完成计算
} else if (s.equals( "(" ))
exp.push( s );// 如果是左括号就直接入栈
else {// 否则就是数字,直接加入rPN中
if (s.charAt( 0 ) == '-') {
rPN.add( s.substring( 1, s.length() ) );
rPN.add( "" + s.charAt( 0 ) );
minus.add( rPN.size() - 1 );//全局变量minus负号索引list集合
} else
rPN.add( s );
}
}
while (!exp.isEmpty()) {// 最后扫描完list后,将栈exp中的字符串依次加入到rPN后缀表达式列表中
rPN.add( exp.pop() );
}
return rPN;
}

计算后缀表达式思路分析图:

接下来处理运算符号:

重复以上动作,直到后缀表达式List为空,即得:


后缀表达式计算代码:


// 后缀表达式list进行计算
public double calcRPN(List<String> rPN) {
Stack<Double> numStack=new Stack<Double>();
boolean flag=false;//用来标记是否处理过负号
for (int i=0; i < rPN.size(); i++) {
if (isOper( rPN.get( i ) )) {
if (rPN.get( i ).equals( "-" )) {//这里判断两次,是为了提高查找负号索引位置的效率
for (int j=0; j < minus.size(); j++) {//遍历后缀表达式集合中记录的负号的索引下标
if (i == minus.get( j )) {//如果rPN后缀表达式的下标等于minus集合中记录的负号索引下标
String min=rPN.get( i );//说明此位置的‘-’号,为负号而不是减号
Double num=numStack.pop();//那么就pop出数栈,和-拼接
numStack.push( Double.parseDouble( min + num ) );
flag=true;
continue;
}
}
if (flag) {
flag=false;
continue;
}
}//下面就是常规计算,一定要思量上面两个continue的作用
double num1=numStack.pop();
double num2=numStack.pop();
numStack.push( calc( num1, num2, rPN.get( i ).charAt( 0 ) ) );
} else {//如果是数字就直接丢到数栈
numStack.push( Double.valueOf( rPN.get( i ) ) );
}
}
return numStack.pop();
}

接下来是完成源代码:


public class ReversePolishNotationCalcDemo {
List<Integer> minus;//用于记录符号出现的索引 public static void main(String[] args) {
String expression="-7.5*6/(-2+(-6.5- -5.22))";// 7.5 - 6.5 5.22 - - *
expression="7.5+-3*8/(7+2)";
ReversePolishNotationCalcDemo rpn=new ReversePolishNotationCalcDemo();
System.out.print( "中缀表达式为:" ); rpn.getList( expression ).forEach( System.out::print ); // for (String s : rpn.getList(expression)) {
// System.out.printf("%s ", s);
// }
System.out.println();
System.out.print( "后缀表达式为:" );
for (String s : rpn.toRPN( expression )) {
System.out.printf( "%s ", s );
}
System.out.println();
double result=rpn.calcRPN( rpn.toRPN( expression ) );
System.out.println( expression.replace( " ", "" ) + " = " + result );
} // 后缀表达式list进行计算
public double calcRPN(List<String> rPN) {
Stack<Double> numStack=new Stack<Double>();
boolean flag=false;
for (int i=0; i < rPN.size(); i++) {
if (isOper( rPN.get( i ) )) {
if (rPN.get( i ).equals( "-" )) {
for (int j=0; j < minus.size(); j++) {
if (i == minus.get( j )) {
String min=rPN.get( i );
Double num=numStack.pop();
numStack.push( Double.parseDouble( min + num ) );
flag=true;
continue;
}
}
if (flag) {
flag=false;
continue;
}
}
double num1=numStack.pop();
double num2=numStack.pop();
numStack.push( calc( num1, num2, rPN.get( i ).charAt( 0 ) ) );
} else {
numStack.push( Double.valueOf( rPN.get( i ) ) );
}
}
return numStack.pop();
} // 中缀表达式转后缀表达式
public List<String> toRPN(String expression) {
List<String> list=getList( expression );
List<String> rPN=new ArrayList<String>();// 用于存放后缀表达式
Stack<String> exp=new Stack<String>();// 用于操作判断符号
minus=new ArrayList<>();
for (String s : list) {// 将中缀表达式每个元素list遍历
if (isOper( s )) {// 如果字符串是+-*/
if (exp.isEmpty())// 如果栈是空,那么就直接入栈
exp.push( s );//-7.5*6/(-2+(-6.5- -5.22))
else if (priority( s ) <= priority( exp.peek() )) {// 如果栈里面有运算符,那么就和栈顶运算符比较优先级
rPN.add( exp.pop() );// 当前运算符优先级小于栈顶运算符,就把栈顶运算符弹出加入到后缀表达式rPN列表中
exp.push( s );// 然后压入当前运算符
} else
exp.push( s ); } else if (s.equals( ")" )) {// 如果当前字符串是‘)’那么就将栈顶字符串加入add到List rPN中,直到栈顶字符串为"("
while (!exp.peek().equals( "(" )) {
rPN.add( exp.pop() );
}
exp.pop();// 一定要记得将左括号(弹出去,代表一对小括号完成计算
} else if (s.equals( "(" ))
exp.push( s );// 如果是左括号就直接入栈
else {// 否则就是数字,直接加入rPN中
if (s.charAt( 0 ) == '-') {
rPN.add( s.substring( 1, s.length() ) );
rPN.add( "" + s.charAt( 0 ) );
minus.add( rPN.size() - 1 );
} else
rPN.add( s );
}
}
while (!exp.isEmpty()) {// 最后扫描完list后,将栈exp中的字符串依次加入到rPN后缀表达式列表中
rPN.add( exp.pop() );
}
System.out.println( "负号出现的索引:" + Arrays.toString( minus.toArray() ) );
return rPN;
} // 表达式转中缀表达式(支持小数点,负数,小括号)
public List<String> getList(String expression) {
List<String> exp=new ArrayList<String>();
String merge="";
int index=0;
char ch, ch1=0;
boolean flag=false;
expression=expression.replace( " ", "" );
while (index != expression.length()) {
ch=expression.substring( index, index + 1 ).charAt( 0 );
merge+=ch;
if (index < expression.length() - 1) {// 防止下标越界
ch1=expression.substring( index + 1, index + 2 ).charAt( 0 );
if (isOper( ch1 )) {
exp.add( merge );
merge="";
flag=false;
} else if (isOper( ch )) {
if (flag == false && exp.size() > 0) {//-7.5*6/(-2+(-6.5- -5.22))
exp.add( merge );
merge="";
flag=false;
}
}
if (ch == '(' && ch1 == '-' || isOper( ch ) && ch1 == '-')
flag=true;
}
index++;
}
exp.add( merge );
return exp;
} public boolean isOper(String oper) {
return oper.equals( "+" ) || oper.equals( "-" ) || oper.equals( "*" ) || oper.equals( "/" );
} public boolean isOper(char oper) {
return oper == '+' || oper == '-' || oper == '*' || oper == '/' || oper == '(' || oper == ')';
} public int priority(String s) {
if (s.equals( "+" ) || s.equals( "-" ))
return 0;
if (s.equals( "*" ) || s.equals( "/" ))
return 1;
return -1;
} public double calc(double num1, double num2, int oper) {
switch (oper) {
case '+':
return num1 + num2;
case '-':
return num2 - num1;
case '*':
return num1 * num2;
case '/':
return num2 / num1;
default:
break;
}
return 0;
}
}

完整源代码

数据结构之栈—强大的四则复杂运算计算器(超过windows自带的科学计算器)【中缀转后缀表达式】的更多相关文章

  1. 栈的应用1——超级计算器(中缀与后缀表达式)C语言

    这里要学的程序主要用来实现一个功能——输入表达式输出结果,也就是一个计算器.效果如下: 这个程序主要有两个步骤:1.把中缀表达式转换为后缀表达式:2.计算后缀表达式的结果. 首先先明白几个问题: 1. ...

  2. Java数据结构和算法(六)——前缀、中缀、后缀表达式

    前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗 ...

  3. Java数据结构和算法(六):前缀、中缀、后缀表达式

    前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗 ...

  4. C++ 使用栈求解中缀、后缀表达式的值

    1. 前言 表达式求值对于有知识积累的你而言,可以通过认知,按运算符的优先级进行先后运算. 但对计算机而言,表达式仅是一串普通的信息而已,需要通过编码的方式告诉计算机运算法则,这个过程中栈起到了至关重 ...

  5. 栈的应用2——超级计算器(中缀与后缀表达式)C语言

    输入中缀表达式输出结果(结果可以是小数,但输入必须是整数)  #include<stdio.h> #include<stdlib.h> //需要两个栈,一个储存结果,一个储存运 ...

  6. 深入浅出数据结构C语言版(8)——后缀表达式、栈与四则运算计算器

    在深入浅出数据结构(7)的末尾,我们提到了栈可以用于实现计算器,并且我们给出了存储表达式的数据结构(结构体及该结构体组成的数组),如下: //SIZE用于多个场合,如栈的大小.表达式数组的大小 #de ...

  7. 【PHP数据结构】栈和队列的应用

    通过栈和队列的学习,我们似乎会感觉到其实数据结构还是非常简单的嘛.当然,这只是一个开始,我们从顺序表.链表开始,到现在的栈和队列,其实都是为了将来在铺路.在树和图的遍历算法中,都可以见到栈和队列的身影 ...

  8. 数据结构(3) 第三天 栈的应用:就近匹配/中缀表达式转后缀表达式 、树/二叉树的概念、二叉树的递归与非递归遍历(DLR LDR LRD)、递归求叶子节点数目/二叉树高度/二叉树拷贝和释放

    01 上节课回顾 受限的线性表 栈和队列的链式存储其实就是链表 但是不能任意操作 所以叫受限的线性表 02 栈的应用_就近匹配 案例1就近匹配: #include <stdio.h> in ...

  9. 数据结构Java实现06----中缀表达式转换为后缀表达式

    本文主要内容: 表达式的三种形式 中缀表达式与后缀表达式转换算法 一.表达式的三种形式: 中缀表达式:运算符放在两个运算对象中间,如:(2+1)*3.我们从小做数学题时,一直使用的就是中缀表达式. 后 ...

随机推荐

  1. django缓存和跨域解决和短信验证码的使用

    缓存 在实际项目中,存在大量的数据检索,比如我们刷微博的时候,刚开始加载速度慢一点,然后第一次加载完毕之后,如果你此时的手机没有网络,但是你发现你的微博还是可以照样刷,但是刷到一定的页面就走不动了,那 ...

  2. Java进阶之心态

    不管什么时候学习都是一个积累的过程,量变才能引起质变.一口吃一个胖子是不存在的,成长的路上没有捷径,只有学到的知识才是我们走向远方道路的基石!

  3. 你所不知道的 C# 中的细节

    前言 有一个东西叫做鸭子类型,所谓鸭子类型就是,只要一个东西表现得像鸭子那么就能推出这玩意就是鸭子. C# 里面其实也暗藏了很多类似鸭子类型的东西,但是很多开发者并不知道,因此也就没法好好利用这些东西 ...

  4. TensorFlow v2.0的基本张量操作

    使用TensorFlow v2.0的基本张量操作 from __future__ import print_function import tensorflow as tf # 定义张量常量 a = ...

  5. 解读windows认证

    0x00 前言 dll劫持的近期忙,没时间写,先给大家写个windows认证的水文. 0x01 windows认证协议 windows上的认证大致分为本地认证,ntlm协议,和Kerberos协议. ...

  6. qW3xT.2,解决挖矿病毒

    在阿里云使用redis,开启了6379端口,但是当时并没有对redis的密码进行设置. 在晚上一点左右.阿里云给我发短信,告诉我服务器出现紧急安全事件.建议登录云盾-态势感知控制台查看详情和处理. 于 ...

  7. Python Count函数的应用

    Python Count函数的应用 通过LeetCode Origin:https://leetcode-cn.com/problems/robot-return-to-origin/ 学会了Pyth ...

  8. Spring Taco Cloud——Controller的创建(含SpringMVC执行过程&SpringBoot&Spring三者解释及关联)

    在记录这次控制器编写前,对于Spring的感觉就是经常提这样代码好简洁,这样好方便,这个是用来干嘛的诸如之类的话. What is Spring ?这是我想问自己的,一直认为是简化代码利于工程的开源框 ...

  9. 电商平台--Mysql主从搭建(2)

    Master上授权从库: ```grant replication slave on *.* to slave1@ip identified by 'password';``` 基于数据库hotcop ...

  10. LoadRunner从入门到实战学习路线(持续更新中...)

    写在前面        我是一个测试工程师,从土木工程行业转行到互联网行业,目前是工作的第三年.平时主要做功能测试,性能测试接触比较少,虽然以前培训的时候学习过一些性能相关的知识,但都是入门初级的知识 ...