一、题目要求
作为新手小白的第一篇博客,我来给大家分享一下我的一个java实验,有很多漏洞还没修改,还望多多包涵。
要求:把中缀表达式转变为后缀表达式,并求值。
代码实现了带括号,多位数,小数,正负数的四则,取余,以及三角函数和反三角函数运算。
二、实验目的
中缀表达式到后缀表达式的转换以及求值,这个实验有助于学生理解表达式的不同表示方式以及如何使用栈数据结构来进行表达式的处理。
目的:
- 理解中缀表达式和后缀表达式的概念;
- 掌握中缀表达式到后缀表达式的转换算法;
- 学习使用栈进行表达式求值:通过实现中缀表达式到后缀表达式的转换,了解如何使用栈这种数据结构来辅助这一过程。后缀表达式的求值也通常涉及到使用栈;
- 加深对操作符优先级的理解:在中缀表达式转换为后缀表达式的过程中,需要处理不同操作符的优先级,这有助于加深对运算符的理解;
- 熟悉基本的表达式求值:学生需要实现后缀表达式的求值算法,这要求他们对基本的数学运算和栈的使用有一定的熟练度。
三、实验分析
大家肯定都知道基本的如何进行中缀转后缀和后缀表达式求值,这里就不再赘述,直接上代码分析。
代码分析:
先通过deal方法对输入的中缀表达式进行操作,包括在每个符号前后插入空格(insert),以方便后续的分割(split)。使用 MiddleToEnd 方法将中缀表达式转换为后缀表达式,使用两个栈(Opstack 和 list)进行操作。将处理后的后缀结果存到arrylist动态数组中,然后输出后缀表达式。
使用Positive_negative方法在输入中的加法和减法运算符前添加0,以处理正负数的情况(加0后的结果不会输出,这部分可以简化,博主懒得修改了)。然后使用calculate方法对后缀表达式进行求值,通过一个数据栈(dataStack)来存储中间结果。
在 main 方法中,通过 Scanner 获取用户输入的中缀表达式,并创建 A 类的实例来进行处理和计算。代码使用StringBuilder类来处理字符串,通过HashMap存储运算符的优先级,简化运算符比较的逻辑。
附中缀转后缀操作:
1.如果遇到操作数时,将其压入s2;
2.如果运算符时,比较其与s1栈顶运算符的优先级;
(1)如果s1为空,或栈顶运算符为左括号“(”,则直接入栈;
(2)否则,若优先级比栈顶运算符高,也将运算符压入s1;
(3)否则,将s1栈顶运算符弹出并压入s2中,再转到4-(1)与s1中新的栈顶运算符相比较;
3.遇到括号时:
(1)如果是左括号“(”,则直接压入s1;
(2)如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号,此时将这一对括号丢弃;
重复步骤1至3,直到达式的最右边;
4.将s1中剩余的运算符依次弹出并压入s2;
后缀求值就不赘述了,相信大家都很清楚。
四、代码实现
我用的是eclipse,要把包名和类名修改一下。代码上有很多注释,直接上代码。
package test_1;
import java.util.Stack;
import java.util.Scanner;
import java.util.ArrayList;
import java.util.HashMap;
//——————————————————————————————————————————————————————————————
//这个代码写的时间较长,实现带括号,多位数,小数,正负数的四则和取余运算,以及三角函数,反三角函数运算
//不足是运算符较少,可以再扩展一些运算符
//-1+(+2+3)*4 (1+2)*((39+1)/40) (1.1+2)*((-1+41)/(40%41))+sin(1+1)
class A{
Stack <String>Opstack =new Stack<>();
ArrayList<String> list=new ArrayList<>();//Arraylist来存放后缀,char和string不兼容
String str;
String []strs;
Stack<String> dataStack = new Stack<>();
//预处理。初始化strings,通过stringbuilder在字符串中每个符号的前后插入空格(如果是"("只在后面加空格,")"加在前面)
//通过split方法以空格为标识符拆分字符串,并存入strings
public void deal(){//( 1.1 + 2 ) * ( ( -1 + 41 ) / ( 40 % 41 ) ) + sin ( 1 + 1 )
int i=0;
StringBuilder stringBuilder=new StringBuilder(str);
while(i< stringBuilder.length()){
if(!Character.isDigit(stringBuilder.charAt(i))){
if(stringBuilder.charAt(i)=='s'||stringBuilder.charAt(i)=='c'||stringBuilder.charAt(i)=='t'||stringBuilder.charAt(i)=='a'){
while(true) {
if(stringBuilder.charAt(i)>='a'&&stringBuilder.charAt(i)<='z')
i++;
else {
stringBuilder.insert(i,' ');
i++;
if(stringBuilder.charAt(i)=='(') {
stringBuilder.insert(i+1,' ');
i++;
}
break;
}
}
} else if(stringBuilder.charAt(i)=='+'||stringBuilder.charAt(i)=='-') {
if(i==0) {
//stringBuilder.insert(i+1,' ');
//i++;
}
else if(i>=2&&stringBuilder.charAt(i-2)=='('){
//stringBuilder.insert(i+1,' ');
//i++;
}
else {
stringBuilder.insert(i, ' ');
stringBuilder.insert(i + 2, ' ');
i = i + 2;
}
} else if(stringBuilder.charAt(i)=='('){
stringBuilder.insert(i+1,' ');
i++;
}else if (stringBuilder.charAt(i)==')'){
stringBuilder.insert(i,' ');// 啊啊啊啊啊啊啊啊啊 检查了很长世间
i++;
}else if(stringBuilder.charAt(i)=='.') { }
else {
stringBuilder.insert(i, ' ');
stringBuilder.insert(i + 2, ' ');
i = i + 2;
}
}
i++;
}
//System.out.println(stringBuilder);
str =stringBuilder.toString();//返回该对象的字符串表示(默认表现形式:类的名称+@+把一个hashcode的值转成16进制的数字)。
strs=str.split(" ");//空格为分隔符
}
// 1.如果遇到操作数时,将其压入s2;
// 2.如果运算符时,比较其与s1栈顶运算符的优先级;
// (1)如果s1为空,或栈顶运算符为左括号“(”,则直接入栈;
// (2)否则,若优先级比栈顶运算符高,也将运算符压入s1;
// (3)否则,将s1栈顶运算符弹出并压入s2中,再转到4-(1)与s1中新的栈顶运算符相比较;
// 3.遇到括号时:
// (1)如果是左括号“(”,则直接压入s1;
// (2)如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号,此时将这一对括号丢弃;
// 重复步骤1至3,直到表达式的最右边;
// 4.将s1中剩余的运算符依次弹出并压入s2;
public void MiddleToEnd() {
HashMap<String, Integer> characterHashMap = new HashMap<String ,Integer>();
characterHashMap.put("+",0);
characterHashMap.put("-",0);
characterHashMap.put("%",1);
characterHashMap.put("*",1);
characterHashMap.put("/",1);
characterHashMap.put("sin",2);
characterHashMap.put("cos",2);
characterHashMap.put("tan",2);
characterHashMap.put("arctan",2);
characterHashMap.put("arcsin",2);
characterHashMap.put("arccos",2);
characterHashMap.put("(",4);
characterHashMap.put(")",4);
//优先级
for(String c:strs){
if(Character.isDigit(c.charAt(0))) {
list.add(c);
}
else if(c.equals("(")) {
Opstack.push(c);
}
else if(c.equals(")")) {
String judge;
judge=Opstack.pop();
if(!judge.equals("(")) {
while(true) {
list.add(judge);//(1+1)/2
judge=Opstack.pop();
if(judge.equals("("))
break;
}
}
}
else {//运算符
if(c.length()>=2&&Character.isDigit(c.charAt(1))){
if(c.charAt(0)=='-'||c.charAt(0)=='+'){
list.add(c);
}
}//正负数
else {
while(true) {
if(Opstack.isEmpty()||Opstack.peek().equals("(")){
Opstack.push(c);
break;
}
else if(characterHashMap.get(c) > characterHashMap.get(Opstack.peek())){
Opstack.push(c);
break;
}else {
list.add(Opstack.pop());
}
}
}
}
}
//将 op中剩余的运算符依次弹出并压入 list
while(!Opstack.isEmpty()){
String n=Opstack.pop();
list.add(n);
}
}
//遍历我们的集合(存放后缀表达式的)将数据暂时放入栈中方便我们操作,然后在遍历过程中进行判断,如果是数据就直接放入栈中,
//如果是运算符就从栈中取出两个数据进行运算,运算结果又放入栈中,直到栈中只存在一个数据时,就是最后的运算结果。
public double calculate() {
//Stack <Double> Calstack =new Stack<>();//用于计算
for (String s:list) {
if (Character.isDigit(s.charAt(0))){
dataStack.push(s);
}else {//运算符
double num1;
double num2;
double res = 0;
if (s.equals("+")){
num1= Double.parseDouble(dataStack.pop());//字符串转为 Double.parseDouble()
num2= Double.parseDouble(dataStack.pop());
res = num1 + num2;
}else if (s.equals("-")) {
num1= Double.parseDouble(dataStack.pop());
num2= Double.parseDouble(dataStack.pop());
res = num2 - num1;
}else if (s.equals("*")) {
num1= Double.parseDouble(dataStack.pop());
num2= Double.parseDouble(dataStack.pop());
res = num1 * num2;
}else if (s.equals("/")) {
num1= Double.parseDouble(dataStack.pop());
num2= Double.parseDouble(dataStack.pop());
res = num2 / num1;
}else if (s.equals("%")) {
num1= Double.parseDouble(dataStack.pop());
num2= Double.parseDouble(dataStack.pop());
res = num2 % num1;
}else if(s.equals("sin")) {
num1= Double.parseDouble(dataStack.pop());
res=Math.sin(num1);
}else if(s.equals("cos")) {
num1= Double.parseDouble(dataStack.pop());
res=Math.cos(num1);
}else if(s.equals("tan")) {
num1= Double.parseDouble(dataStack.pop());
res=Math.tan(num1);
}else if(s.equals("arcsin")) {
num1= Double.parseDouble(dataStack.pop());
res=Math.asin(num1);
}else if(s.equals("arccos")) {
num1= Double.parseDouble(dataStack.pop());
res=Math.acos(num1);
}else if(s.equals("arctan")) {
num1= Double.parseDouble(dataStack.pop());
res=Math.atan(num1);
}
else{
throw new RuntimeException("运算符异常!");
}
dataStack.push("" + res);
}
}
//返回结果
return Double.parseDouble(dataStack.pop());
}
public void Positive_negative() {
StringBuilder pn=new StringBuilder(str);
int i=0;
while(i< pn.length()){
if(pn.charAt(i)=='+'||pn.charAt(i)=='-') {
if(i==0||pn.charAt(i-1)=='(') {
pn.insert(i,"0");
i++;
}
}
i++;
}
str=pn.toString();//返回该对象的字符串表示(默认表现形式:类的名称+@+把一个hashcode的值转成16进制的数字)。
//System.out.println(str);
}//正负只需要加个0就行了,0不能输出来
}
public class Solve {
public static void main(String []args) {
Scanner in =new Scanner(System.in);
A a=new A();
A b=new A();
a.str = in.nextLine();
b.str=a.str;
a.deal();
a.MiddleToEnd();
for (int i=0;i<a.list.size();i++) {
System.out.print(a.list.get(i)+" ");
}
System.out.println();
b.Positive_negative();
b.deal();
b.MiddleToEnd();
// for (int i=0;i<b.list.size();i++) {
// System.out.print(b.list.get(i)+" ");
// }
// System.out.println();
double x=b.calculate();
System.out.println(x);
in.close();
}
}
五、实验结果
六、实验总结
代码实现的功能较多,采用字符串和判断语句,实现的较为完整。
改进意见:
- 添加更多的算术函数,以提高代码的灵活性和适用性。
- 考虑对输入表达式进行错误处理,如空格匹配检查等。
- 增加注释,以提高代码的竞争力和可维护性。
- calculate方法中可以将if else语句换成switch语句,更整洁。
希望这篇博客能对大家有所帮助。