文章目录
1.几种表达式的了解
1.1中缀表达式
1.1.1 定义
所谓的中缀表达式,就是我们平常见到式子,比如 ( 3 + 4 ) * 5 - 6
1.1.2 计算
中缀表达式的计算,通常是转换成后缀表达式进行计算
1.2前缀表达式
1.2.1 定义
前缀表达式又叫做波兰表达式,操作运算符位于数字前面。
1.2.2 计算
前缀表达式的计算:新建一个栈,然后对表达式从右到左进行遍历,如果遇到数字,那么将数字压入栈中,如果遇到的是操作运算符,那么就从栈中跳出两个数字num1,num2,并进行相应的运算,此时需要注意相关的运算法则,比如除法中的除数不可以为0,然后将结果再次压入到栈中。注意如果是 - 号、/ 号 , % 号,那么必须是num1 - num2,num1 / num2,num1 % num2,而+号,*号就不用区分,因为结果都一样的嘛,为什么会这样子呢?请看图解。
遍历结束之后,栈中只剩下一个元素,那么这个元素就是表达式的值,将其从栈中跳出即可。
1.3后缀表达式
1.3.1 定义
后缀表达式又叫做逆波兰表达式,操作运算符位于数字之后。比如(3 + 4 ) * 5 - 6 的后缀表达式为 3 4 + 5 * 6 - 。
几种常见的中缀表达式对应的后缀表达式
1.3.2 计算
后缀表达式的计算:新建一个栈,然后对表达式从左到右进行遍历,刚好和前缀表达式相反,如果遇到数字,那么将数字压入栈中,如果遇到的是操作运算符,那么就从栈中跳出两个数字num1,num2,并进行相应的运算,此时需要注意相关的运算法则,比如除法中的除数不可以为0,然后将结果再次压入到栈中。注意如果是 - 号、/ 号 , % 号,那么必须是num2 - num1,num2 / num1,num2 % num1,而+号,*号就不用区分,因为结果都一样的嘛,为什么会这样子呢?请看图解。
遍历结束之后,栈中只剩下一个元素,那么这个元素就是表达式的值,将其从栈中跳出即可。
2.中缀表达式转前缀、后缀表达式,并计算其值
中缀表达式转换其他两种表达式都需要用到栈。
2.1 中缀表达式转后缀表达式
2.1.1 思路
我的思路是通过两个栈来实现的,如果有更好的方法,还请指教。
过程:
1)从左到右遍历表达式
2)如果遇到数字,那么就要将其压入到栈array中。不过需要注意多位数字的问题。
3)如果遇到的是字符,那么将分几种情况来讨论:
①如果存放字符的栈stringArray为空,或者栈顶的符号是左括号,或者当前的符号是左括号,那么直接将当前符号压入栈stringArray。
②否则,如果当前符号是右括号,那么需要通过循环来找到左括号,找到左括号之后,将其跳出栈,从而消除括号。注意在每一次循环中, 需要括号内的符号压入到栈array中。
③如果都不符合上面的情况,那么要将当前符号和栈顶符号比较优先级。如果当前符号的优先级大于栈顶符号的优先级,那么直接将当前符号压入栈stringArray中,否则,不断将stringArray的栈顶符号跳出,然后将其压入到栈array中,直到当前符号的优先级大于栈顶符号的优先级,才可以将当前符号压入栈stringArray中。
4)将array中的所有元素都压入到栈arrayString中。
由图可以知道,将array中的元素压入到arrayString中,遍历arrayString得到的才是后缀表达式,否则,如果将arrayString的元素压入到array,遍历array,得到的是后缀表达式的倒序,此时还需要将其转换过来,就显得玛法些。
5)遍历arrayString,将arrayString的元素赋给字符串列表List,得到的字符串列表就是是后缀表达式。
可能有同学会问,可以是字符串数组吗?我认为是不可以的,如果定义的字符串数组的长度是原来表达式的长度,可能会报错,因为有括号()的存在,即使没有括号,还要考虑多位数字的存在,从而影响了前缀表达式的长度不再是原来中缀表达式的长度了。还有人会想,那么我定义字符串数组长度很大不就行了,感觉上是可以的,但是会占据比较大的空间,而且进行运算的时候,也会比较麻烦,因为要统计实际上数组中字符的个数,所以综合来说,我觉得还是用列表好一些,这只是我的个人见解,如果有什么不对的地方,请指正。
2.1.2 代码实现:
这个代码可以实现多位数字的运算,但是不可以实现小数的,因为进行计算的时候是整形,返回的也是整形,如果有小数,就没有办法得到正确结果。
需要补充的地方是我这里字符串的输入格式一定是3 + 5 * 6 - 1,每一个字符间用一个空格隔开,因为主函数那里通过方法split(),将字符串分割成字符串,这样就不需要在定义一个变量用于拼接多位数字了。
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class SuffixExpressionTest {
public Stack<String> array;
public Stack<String> stringArray;
public int length ;
/**
* 新建两个栈
* @param infixExpression
*/
public SuffixExpressionTest(String[] infixExpression) {
array = new Stack<String>();
stringArray = new Stack<String>();
length = infixExpression.length;
}
public List<String> getSuffixExpression(String[] infixExpression) {
if(infixExpression == null)
throw new RuntimeException("字符串为空, 请输入字符串");
List<String> string = new ArrayList<String>();
//从左到右开始遍历字符串
for(int i = 0; i<length; i++) {
String ch = infixExpression[i];//获得下标为i的字符串
//判断是否为基本运算符
if(isOper(ch)) {
//如果是基本运算符,那么分情况讨论
if(stringArray.empty() || stringArray.peek().equals("(")) {
//如果栈为空, 或者栈顶是左括号,直接入栈
stringArray.push(ch);
}else if(ch.equals("(")) {
//如果当前符号是左括号,那么直接入栈
stringArray.push(ch);
}else if(ch.equals(")")) {
//如果当前符号是右括号,那么就将从栈S1中跳出2个数字,然后栈S2中跳出一个符号,进行相应的运算,将结果压入栈S1
//重复上述操作,直到遇到左括号
while(!stringArray.peek().equals("(")) {
String temp = stringArray.pop();
array.push(temp);
}
stringArray.pop();//将左括号从栈中跳出,消除左括号
}else {
//如果当前符号都不符合上面的几种情况,那么比较优先级
int level_current = priority(ch);
int level_peek = priority(stringArray.peek());
if(level_current > level_peek) {
stringArray.push(ch);//如果当前符号的优先级大于栈顶的优先级,那么直接将符号压入栈中
}else {
/*
如果当前符号的优先级小于等于栈顶符号的优先级,那么
将栈顶符号跳出,然后将这个跳出的栈顶符号压入到
array栈中,重复上述操作,直到栈顶符号的优先级小于
当前符号的优先级或者栈为空的时候,才将当前符号压入
到stringArray栈中
*/
while(level_current <= level_peek){
String temp = stringArray.pop();
array.push(temp);
if(stringArray.empty())
break;//如果stringArray栈为空,那么退出循环
level_peek = priority(stringArray.peek());
}
stringArray.push(ch);//当前符号压入栈S2
}
}
}else {
array.push(ch);
}
}
//结束遍历之后,判断S2是否为空,如果不为空,就将其所有元素压入栈S1中
while(!array.empty()) {
stringArray.push(array.pop());//将栈S1中的数字压入栈S2中,那么输出栈S2就是后缀表达式
}
//获取后缀表达式
while(!stringArray.empty()) {
string.add(stringArray.pop());
}
return string;//返回后缀表达式
}
/**
* 计算后缀表达式的值,通过配合栈来实现
* 如果遇到的数字,那么就将数字压入栈中,否则,跳出两个数字,进行相应的运算,并将结果压入栈中,最后站还有一个数,那么这个数就是表达式的值
* @param suffixExpression
* @return
*/
public int getSuffixExpressionResult(List<String> suffixExpression) {
int result = 0;
if(suffixExpression == null)
throw new RuntimeException("后缀表达式为空,注意检查");
//从左到右开始遍历
for(int i = 0; i<suffixExpression.size(); i++) {
String str = suffixExpression.get(i);
if(isOper(str)) {
//如果当前的字符是基本运算符,那么就从栈中条出两个数字,并进行相应的运算,之后将结果压入栈中
//由于是从左到右开始遍历,那么应该是num2 - num1,num2 / num1
int num1 = Integer.parseInt(array.pop());
int num2 = Integer.parseInt(array.pop());
if(str.equals("+"))
array.push(String.valueOf(num2 + num1));
else if(str.equals("-"))
array.push(String.valueOf(num2 - num1));
else if(str.equals("*"))
array.push(String.valueOf(num2 * num1));
else if(str.equals("/")) {
//注意除法的运算规则
if(num1 == 0)
throw new RuntimeException("除数不可以是0,运算式错误,注意检查");
else
array.push(String.valueOf(num2 / num1));
}
else{
if(num1 == 0)
throw new RuntimeException("除数不可以为0,表达式错误,不符合运算规则,注意检查");
array.push(String.valueOf(num2 % num1));
}
}else {
array.push(str);
}
}
return Integer.parseInt(array.pop());
}
/**
* 比较优先级
* @param ch
* @return
*/
public int priority(String ch) {
if(ch.equals("+") || ch.equals("-"))
return 1;
else if(ch.equals("*") || ch.equals("/"))
return 2;
else if(ch.equals("%"))
return 3;
else
throw new RuntimeException("该符号不是基本运算符号,表达式错误,注意检查");
}
/**
* 判断是否为基本运算符号
* @param ch
* @return
*/
public boolean isOper(String ch) {
return (ch.equals("+") || ch.equals("-") || ch.equals("*") || ch.equals("/") || ch.equals("%") || ch.equals("(") || ch.equals(")"));
}
}
//主函数
import java.util.List;
import java.util.Scanner;
public class SuffixExpressionTestApp {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String infixExpression = sc.nextLine();
String[] toSuffixExpression = infixExpression.split(" ");//将字符串分割成字符串数组
SuffixExpressionTest test = new SuffixExpressionTest(toSuffixExpression);
try{
List<String> suffixExpression = test.getSuffixExpression(toSuffixExpression);//获得后缀表达式
System.out.println(infixExpression +" 的后缀表达式为 " +suffixExpression);
int result = test.getSuffixExpressionResult(suffixExpression);
System.out.println(suffixExpression +" 的结果为 " + result);
}catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
实现小数的基本一样的代码,不同的地方就是在进行运算的地方,它是将String类型转换成double类型,返回的也是double类型。
代码如下:
/**
* 计算后缀表达式的值,通过配合栈来实现
* 如果遇到的数字,那么就将数字压入栈中,否则,跳出两个数字,进行相应的运算,并将结果压入栈中,最后站还有一个数,那么这个数就是表达式的值
* @param suffixExpression
* @return
*/
public double getSuffixExpressionResult(List<String> suffixExpression) {
int result = 0;
if(suffixExpression == null)
throw new RuntimeException("后缀表达式为空,注意检查");
//从左到右开始遍历
for(int i = 0; i<suffixExpression.size(); i++) {
String str = suffixExpression.get(i);
if(isOper(str)) {
//如果当前的字符是基本运算符,那么就从栈中条出两个数字,并进行相应的运算,之后将结果压入栈中
//由于是从左到右开始遍历,那么应该是num2 - num1,num2 / num1
double num1 = Double.parseDouble(array.pop());
double num2 = Double.parseDouble(array.pop());
if(str.equals("+"))
array.push(String.valueOf(num2 + num1));
else if(str.equals("-"))
array.push(String.valueOf(num2 - num1));
else if(str.equals("*"))
array.push(String.valueOf(num2 * num1));
else if(str.equals("/")) {
//注意除法的运算规则
if(num1 == 0)
throw new RuntimeException("除数不可以是0,运算式错误,请注意检查,重新输入");
else
array.push(String.valueOf(num2 / num1));
}
else {
if(num1 == 0)
throw new RuntimeException("取模时除数不可以为0,运算式错误,请检查表达式,重新输入");
array.push(String.valueOf(num2 % num1));
}
}else {
array.push(str);
}
}
return Double.parseDouble(array.pop());
}
表达式的输入不需要用空格来隔开每一个字符的代码,如果有空格,就会报错,(同样这个代码是可以实现多位数字的,不过不可以实现含有小数的表达式,如果要实现含有小数,道理和上面一样的,在计算后缀表达式的时候,将每一个数字从栈中跳出,然后转换成double类型,然后返回的也是double就可以了,这里就不写它的代码了):
//主函数
import java.util.List;
import java.util.Scanner;
public class SuffixExpressionTestApp {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String infixExpression = sc.next();//输入中缀表达式(不可以用空格将每一个字符隔开,否则会报错)
SuffixExpressionTest test1 = new SuffixExpressionTest(infixExpression);
try{
List<String> suffixExpression = test1.getSuffixExpression(infixExpression);//获得后缀表达式
System.out.println(infixExpression +" 的后缀表达式为 " +suffixExpression);
int result = test1.getSuffixExpressionResult(suffixExpression);
System.out.println(suffixExpression +" 的结果为 " + result);
}catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
//调用这个类的方法,可以获得后缀表达式
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class SuffixExpressionTest {
public Stack<String> array;
public Stack<String> stringArray;
public int length ;
//新建两个栈
public SuffixExpressionTest(String infixExpression) {
array = new Stack<String>();
stringArray = new Stack<String>();
}
//获得后缀表达式
public List<String> getSuffixExpression(String infixExpression) {
if(infixExpression == null)
throw new RuntimeException("字符串为空, 请输入字符串");
List<String> string = new ArrayList<String>();
String keepNum = "";//用来拼接多位数字的
//从左到右开始遍历字符串
for(int i = 0; i<infixExpression.length(); i++) {
String ch = infixExpression.substring(i,i+1);//获得下标为i的字符串
//判断是否为基本运算符
if(isOper(ch)) {
//如果是基本运算符,那么分情况讨论
if(stringArray.empty() || stringArray.peek().equals("(")) {
//如果栈为空, 或者栈顶是左括号,直接入栈
stringArray.push(ch);
}else if(ch.equals("(")) {
//如果当前符号是左括号,那么直接入栈
stringArray.push(ch);
}else if(ch.equals(")")) {
//如果当前符号是右括号,那么就将从栈S1中跳出2个数字,然后栈S2中跳出一个符号,进行相应的运算,将结果压入栈S1
//重复上述操作,直到遇到左括号
while(!stringArray.peek().equals("(")) {
String temp = stringArray.pop();
array.push(temp);
}
stringArray.pop();//将左括号从栈中跳出,消除左括号
}else {
//如果当前符号都不符合上面的几种情况,那么比较优先级
if(priority(ch) > priority(stringArray.peek())) {
stringArray.push(ch);//如果当前符号的优先级大于栈顶的优先级,那么直接将符号压入栈中
}else {
String temp = stringArray.pop();
array.push(temp);
stringArray.push(ch);//当前符号压入栈S2
}
}
}else {
//如果当前符号是数字,那么注意多位数字的拼接
keepNum = ch;
int j;
for(j = i + 1; j<infixExpression.length(); j++) {
String str = infixExpression.substring(j, j+ 1);
if(isOper(str)) {
break;
}
keepNum += str;//拼接多位数字
}
array.push(keepNum);
keepNum = "";//将其重置为""
i = j - 1;//注意这一步是必须的,因为退出循环的时候j对应的是非数字的下标,而在外部循环中又有i++,所以i = j - 1才可以,如果没有的话,那么就会少了一些字符
}
}
//结束遍历之后,判断array是否为空,如果不为空,就将其所有元素压入栈stringArray中
while(!array.empty()) {
stringArray.push(String.valueOf(array.pop()));//将栈array中的数字压入栈stringArray中,那么输出栈stringArray就是后缀表达式
}
//获取后缀表达式
while(!stringArray.empty()) {
string.add(stringArray.pop());
}
return string;//返回后缀表达式
}
/**
* 计算后缀表达式的值,通过配合栈来实现
* 如果遇到的数字,那么就将数字压入栈中,否则,跳出两个数字,进行相应的运算,并将结果压入栈中,最后站还有一个数,那么这个数就是表达式的值
* @param suffixExpression
* @return
*/
public int getSuffixExpressionResult(List<String> suffixExpression) {
int result = 0;
if(suffixExpression == null)
throw new RuntimeException("后缀表达式为空,注意检查");
//从左到右开始遍历
for(int i = 0; i<suffixExpression.size(); i++) {
String str = suffixExpression.get(i);
if(isOper(str)) {
//如果当前的字符是基本运算符,那么就从栈中条出两个数字,并进行相应的运算,之后将结果压入栈中
//由于是从左到右开始遍历,那么应该是num2 - num1,num2 / num1
int num1 = Integer.parseInt(array.pop());
int num2 = Integer.parseInt(array.pop());
if(str.equals("+"))
array.push(String.valueOf(num2 + num1));
else if(str.equals("-"))
array.push(String.valueOf(num2 - num1));
else if(str.equals("*"))
array.push(String.valueOf(num2 * num1));
else if(str.equals("/")) {
//注意除法的运算规则
if(num1 == 0)
throw new RuntimeException("除数不可以是0,运算式错误,注意检查");
else
array.push(String.valueOf(num2 / num1));
}else if(str.equals("%")) {
if(num1 == 0)
throw new RuntimeException("除数不可以为0,表达式错误,不符合运算规则,注意检查");
array.push(String.valueOf(num2 % num1));
}
}else {
array.push(str);
}
}
return Integer.parseInt(array.pop());
}
/**
* 比较优先级
* @param ch
* @return
*/
public int priority(String ch) {
if(ch.equals("+") || ch.equals("-"))
return 1;
else if(ch.equals("*") || ch.equals("/"))
return 2;
else if(ch.equals("%"))
return 3;
else
throw new RuntimeException("该符号不是基本运算符号,表达式错误,注意检查");
}
/**
* 判断是否为基本运算符号
* @param ch
* @return
*/
public boolean isOper(String ch) {
return (ch.equals("+") || ch.equals("-") || ch.equals("*") || ch.equals("/") || ch.equals("%") || ch.equals("(") || ch.equals(")"));
}
}
2.2 中缀表达式转前缀表达式
2.2.1 思路
将中缀表达式转换为前缀表达式过程:
1)定义两个栈,其中一个栈是array,另一个栈是arrayString。
2)从右到左开始遍历表达式。
3)遇到数字,就将其压入到栈array,注意多位数字的情况。
4)遇到的是基本运算符,那么分几种情况讨论:
①如果栈arrayString为空 ,或者栈顶符号是右括号 ) , 或者当前符号是右括号 ) ,那么直接将当前符号压入栈arrayString中。
②如果当前符号是左括号,那么就遍历栈arrayString,直到栈顶的符号不是右括号,否则就循环,同时再每一次循环中,要将栈arrayString中符号压入到栈array中,当结束循环以后,注意还要消除右括号,也就是再次将S2的符号跳出一次。
③如果当前的符号都不符合上面几种情况,那么将当前符号和栈顶的符号进行比较优先级:
(1)如果当前符号的优先级大于或者等于栈顶的优先级,那么直接将符号压入栈arrayString。
(2)否则,如果当前符号的优先级小于栈顶的优先级,那么先从栈arrayString跳出栈顶的符号,然后将其压入到栈array中,最后将当前符号压入到栈arrayString中。
正确进行比较优先级时的结果
错误进行比较优先级(就是和转后缀表达式一样,当前符号的优先级大于栈顶符号的优先级,直接将当前符号压入栈arrayString,否则,先将栈顶符号从栈arrayString跳出,然后将其压入栈array,最后将当前符号压入栈arrayString)的结果:
5)遍历结束之后,将arrayString中的所有元素压入到array中。
由图解可知,将栈arrayString的元素都压入栈array,将array遍历输出的就是前缀表达式。如果将栈array的元素都压入栈arrayString,然将arrayString遍历输出的是前缀表达式的倒序。
6)遍历array,将其元素赋给一个字符串列表List,那么这个列表就是要求的前缀表达式。(关于为什么是列表而不是数组的讨论,上面中缀表达式转后缀表达式已经讨论过了,这里就不说了哈)。
2.2.2 代码实现:
1)表达式要求是每一个字符都用一个空格间隔开,这样的话,在处理多位数字(没有小数)的问题上,不需要定义一个变量用于拼接,也不需要在一次循环。如果没有空格的话,会出错。
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PrefixExpression {
public Stack<String> array;
public Stack<String> stringArray;
public int length;
public PrefixExpression(String[] infixExpression) {
length = infixExpression.length;
array = new Stack<String>();
stringArray = new Stack<String>();
}
public List<String> getPrefixExpression(String[] infixExpression) {
if(infixExpression == null) {
throw new RuntimeException("链表为空,请输入字符串");
}
List<String> str = new ArrayList<String>();
//从右到左开始遍历字符串
for(int i = length - 1; i >= 0; i --) {
String ch = infixExpression[i];//获得当前的字符
//判断当前字符是否为基本运算符
if(isOper(ch)) {
//如果栈为空,或者栈顶的符号是右括号,或者当前符号是右括号,那么直接入栈
if(stringArray.empty() || ch.equals(")") || stringArray.peek().equals(")") )
stringArray.push(ch);
else if(ch.equals("(")) {
//如果当前符号是左括号,那么遍历,将栈stringArray中的元素压入到栈array中去,直到遇到右括号
while(!stringArray.peek().equals(")")) {
array.push(stringArray.pop());
}
stringArray.pop();//消除右括号
}else {
//如果当前符号都不符合上面几种情况,那么比较优先级
if(priority(ch) >= priority(stringArray.peek())) {
//如果当前符号的优先级比栈顶的优先级高或者相等,那么就将直接入栈
stringArray.push(ch);
}else {
//否则,如果当前符号的优先级小于栈顶的优先级,那么先将栈顶的符号压入到栈array,然后才将当前符号压入到stringArray中
array.push(stringArray.pop());
stringArray.push(ch);
}
}
}else {
array.push(ch);//将数字压入到栈S1
}
}
while(!stringArray.empty()) {//将stringArray的所有元素压入到栈array中
array.push(stringArray.pop());
}
while(!array.empty()) {//遍历栈array,,然后将其赋给字符串列表
str.add(array.pop());
}
return str;//返回前缀表达式
}
/**
*计算前缀表达式的值 --通过栈来实现,从右到左开始遍历,如果遇到的是数字,那么将数字压入栈中,否则遇到的是运算符,那么就进行运算(注意运算法则)
* @param prefixExpression 前缀表达式
* @return 前缀表达式的结果
*/
public int getPrefixExpressionResult(List<String> prefixExpression) {
//从右到左开始遍历
for(int i = prefixExpression.size() - 1; i >= 0; i--) {
String ch = prefixExpression.get(i);
if(isOper(ch)) {
//如果是运算符,那么将跳出两个数字,进行相应的运算,然后将结果压入栈中
int num1 = Integer.parseInt(array.pop());
int num2 = Integer.parseInt(array.pop());
if(ch.equals("-"))
array.push(String.valueOf(num1 - num2));//注意将结果转换成整形,同时注意是num1 - num2,而不是num2 - num1,下面同理
else if(ch.equals("+"))
array.push(String.valueOf(num2 + num1));//注意将结果转换成整形
else if(ch.equals("*"))
array.push(String.valueOf(num1 * num2));//注意将结果转换成整形
else if(ch.equals("/")) {
if(num2 == 0)
throw new RuntimeException("除数不可以为0,表达式错误,不符合运算规则,注意检查");
array.push(String.valueOf(num1 / num2));//注意将结果转换成整形
}else if(ch.equals("%")) {
if(num2 == 0)
throw new RuntimeException("取模时除数不可以为0,表达式错误,不符合运算规则,注意检查");
array.push(String.valueOf(num1 % num2));//注意将结果转换成整形
}
}else {
array.push(ch);//如果不是运算符,那么就将其压入栈中 --考虑多位数字的存在
}
}
return Integer.parseInt(array.pop());
}
/**
* 比较优先级
* @param ch
* @return
*/
public int priority(String ch) {
if(ch.equals("+") || ch.equals("-"))
return 1;
else if(ch.equals("*") || ch.equals("/"))
return 2;
else if(ch.equals("%"))
return 3;
else
throw new RuntimeException("该符号不是基本运算符号,表达式错误,注意检查");
}
/**
* 判断是否为基本运算符号
* @param ch
* @return
*/
public boolean isOper(String ch) {
return (ch.equals("+") || ch.equals("-") || ch.equals("*") || ch.equals("/") || ch.equals("%") || ch.equals("(") || ch.equals(")"));
}
}
//主函数
import java.util.List;
import java.util.Scanner;
public class PrefixExpressionApp {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String infixExpression = sc.nextLine();
String[] toInfixExpression = infixExpression.split(" ");//将字符串分割成字符串数组
PrefixExpression test = new PrefixExpression(toInfixExpression);
List<String> prefixExpression = test.getPrefixExpression(toInfixExpression);//获得前缀表达式
System.out.print(infixExpression +" 的前缀表达式为: ");
System.out.println(prefixExpression);
try {
int result = test.getPrefixExpressionResult(prefixExpression);
System.out.println(prefixExpression +" 的结果为 "+ result);
}catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
表达式输入要有一个空将每一个字符间隔开时,处理含有小数时方法和处理后缀表达式中含有小数的方法一样,都是在进行运算的时候,将每一个数字从栈中跳出,然后转化成double类型,在进行运算,最后返回double类型就可以了,这里就不写了)
2)表达式没有空格(处理多位数字,不含小数):
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PrefixExpression {
public Stack<String> array;
public Stack<String> stringArray;
public int length;
public PrefixExpression(String infixExpression) {
length = infixExpression.length();
array = new Stack<String>();
stringArray = new Stack<String>();
}
public List<String> getPrefixExpression(String infixExpression) {
if(infixExpression == null) {
throw new RuntimeException("链表为空,请输入字符串");
}
List<String> str = new ArrayList<String>();
//从右到左开始遍历字符串
for(int i = length - 1; i >= 0; i --) {
String ch = infixExpression.substring(i,i+1);//获得当前的字符
//判断当前字符是否为基本运算符
if(isOper(ch)) {
//如果栈为空,或者栈顶的符号是右括号,或者当前符号是右括号,那么直接入栈
if(stringArray.empty() || ch.equals(")") || stringArray.peek().equals(")") )
stringArray.push(ch);
else if(ch.equals("(")) {
//如果当前符号是左括号,那么遍历,将栈stringArray中的元素压入到栈array中去,直到遇到右括号
while(!stringArray.peek().equals(")")) {
array.push(stringArray.pop());
}
stringArray.pop();//消除右括号
}else {
//如果当前符号都不符合上面几种情况,那么比较优先级
if(priority(ch) >= priority(stringArray.peek())) {
//如果当前符号的优先级比栈顶的优先级高或者相等,那么就将直接入栈
stringArray.push(ch);
}else {
//否则,如果当前符号的优先级小于栈顶的优先级,那么先将栈顶的符号压入到栈array,然后才将当前符号压入到stringArray中
array.push(stringArray.pop());
stringArray.push(ch);
}
}
}else {
//如果是数字,那么注意多位数字的拼接
int result = Integer.parseInt(ch);
int k,j;
for(j = i - 1,k = 10; j>= 0; j--,k *= 10) {
String temp = infixExpression.substring(j,j + 1);
if(isOper(temp)) {
break;
}
//由于是从右到左开始遍历的,所以不可以直接是keepNum += temp,因为如果是这样拼接多位数字的话会错误,因为将14倒过来了,变成了41
result += Integer.parseInt(temp) * k;
}
array.push(String.valueOf(result));//将数字压入到栈S1
i = j + 1;//这一步是必须,(原因和转后缀表达式那里类似,关键是因为遍历的顺序,所以是加还是减,要看遍历的顺序,这里就不说了哈)
}
}
while(!stringArray.empty()) {
array.push(stringArray.pop());
}
while(!array.empty()) {
str.add(array.pop());
}
return str;
}
/**
*计算前缀表达式的值 --通过栈来实现,从右到左开始遍历,如果遇到的是数字,那么将数字压入栈中,否则遇到的是运算符,那么就进行运算(注意运算法则)
* @param prefixExpression 前缀表达式
* @return 前缀表达式的结果
*/
public int getPrefixExpressionResult(List<String> prefixExpression) {
//从右到左开始遍历
for(int i = prefixExpression.size() - 1; i >= 0; i--) {
String ch = prefixExpression.get(i);
if(isOper(ch)) {
//如果是运算符,那么将跳出两个数字,进行相应的运算,然后将结果压入栈中
int num1 = Integer.parseInt(array.pop());
int num2 = Integer.parseInt(array.pop());
if(ch.equals("-"))
array.push(String.valueOf(num1 - num2));//注意将结果转换成整形
else if(ch.equals("+"))
array.push(String.valueOf(num2 + num1));//注意将结果转换成整形
else if(ch.equals("*"))
array.push(String.valueOf(num1 * num2));//注意将结果转换成整形
else if(ch.equals("/")) {
if(num2 == 0)
throw new RuntimeException("除数不可以为0,表达式错误,不符合运算规则,注意检查");
array.push(String.valueOf(num1 / num2));//注意将结果转换成整形
}else if(ch.equals("%")) {
if(num2 == 0)
throw new RuntimeException("取模时除数不可以为0,表达式错误,不符合运算规则,注意检查");
array.push(String.valueOf(num1 % num2));//注意将结果转换成整形
}
}else {
array.push(ch);//如果不是运算符,那么就将其压入栈中 --考虑多位数字的存在
}
}
return Integer.parseInt(array.pop());
}
/**
* 比较优先级
* @param ch
* @return
*/
public int priority(String ch) {
if(ch.equals("+") || ch.equals("-"))
return 1;
else if(ch.equals("*") || ch.equals("/"))
return 2;
else if(ch.equals("%"))
return 3;
else
throw new RuntimeException("该符号不是基本运算符号,表达式错误,注意检查");
}
/**
* 判断是否为基本运算符号
* @param ch
* @return
*/
public boolean isOper(String ch) {
return (ch.equals("+") || ch.equals("-") || ch.equals("*") || ch.equals("/") || ch.equals("%") || ch.equals("(") || ch.equals(")"));
}
}
//主函数
import java.util.List;
import java.util.Scanner;
public class PrefixExpressionApp {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String infixExpression = sc.next();//输入的是没有空格的表达式
PrefixExpression test = new PrefixExpression(infixExpression);
List<String> prefixExpression = test.getPrefixExpression(infixExpression);//获得前缀表达式
System.out.print(infixExpression +" 的前缀表达式为: ");
System.out.println(prefixExpression);
try {
int result = test.getPrefixExpressionResult(prefixExpression);
System.out.println(prefixExpression +" 的结果为 "+ result);
}catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
表达式没有用一个空格隔开每一个字符的,处理多为数字的时候,需要注意小数的拼接(因为他是从右到左开始遍历,那么就不像后缀表达式那样,通过用一个字符串就可以实现小数的拼接),而在运算的时候,和上面的方式一样,都是将从栈中跳出数字,然后将其类型转换成double类型,最后返回double类型。
拼接小数时的代码(我将返回前缀表达式的代码都写了,重点看拼接数字那里的代码就好了):
public List<String> getPrefixExpression(String infixExpression) {
if(infixExpression == null) {
throw new RuntimeException("链表为空,请输入字符串");
}
List<String> str = new ArrayList<String>();
//从右到左开始遍历字符串
for(int i = length - 1; i >= 0; i --) {
String ch = infixExpression.substring(i,i+1);//获得当前的字符
//判断当前字符是否为基本运算符
if(isOper(ch)) {
//如果栈为空,或者栈顶的符号是右括号,或者当前符号是右括号,那么直接入栈
if(stringArray.empty() || ch.equals(")") || stringArray.peek().equals(")") )
stringArray.push(ch);
else if(ch.equals("(")) {
//如果当前符号是左括号,那么遍历,将栈S2中的元素压入到栈S1中去,直到遇到右括号
while(!stringArray.peek().equals(")")) {
array.push(stringArray.pop());
}
stringArray.pop();//消除左括号
}else {
//如果当前符号都不符合上面几种情况,那么比较优先级
if(priority(ch) >= priority(stringArray.peek())) {
//如果当前符号的优先级比栈顶的优先级高或者相等,那么就将直接入栈
stringArray.push(ch);
}else {
//当前符号的优先级小于栈顶的优先级,那么先将栈顶的符号压入到栈S1,然后才将当前符号压入到S2中
array.push(stringArray.pop());
stringArray.push(ch);
}
}
}else {
//如果是数字,那么注意多位数字的拼接
double result = Double.parseDouble(ch);
boolean con = false;//标记是否含有小数点
int k,j,count = 0;
for(j = i - 1,k = 10; j>= 0; j--) {
String temp = infixExpression.substring(j,j + 1);
if(isOper(temp)) {
break;
}else if(temp.equals(".")){
con = true;
continue;
}else {//由于是从右到左开始遍历的,所以不可以直接是keepNum += temp,因为如果是这样拼接多位数字的话会错误,因为将14倒过来了,变成了41
if(con){
//如果是小数,那么看小数点之前有多少位数字
count ++;
}
result += Double.parseDouble(temp) * k;
k *= 10;
}
}
if(con)//如果含有小数点,需要将结果整理,从而得到小数
result /= (k / Math.pow(10,count));//这个可以举例子说明的,比如如果数字是77.9,那么在经过遍历拼接之后,result = 779,count = 2,k = 1000,那么经过这一步就是77.9了
array.push(String.valueOf(result));//将数字压入到栈array
i = j + 1;//这一步是必须,(原因和转后缀表达式那里类似,关键是因为遍历的顺序,所以是加还是减,要看遍历的顺序,这里就不说了哈)
}
}
while(!stringArray.empty()) {
array.push(stringArray.pop());
}
while(!array.empty()) {
str.add(array.pop());
}
return str;
}