我们的表达式可以分为中缀表达式、前缀表达式、后缀表达式。在我们的上一篇博客中说了用Java栈来模拟实现综合计算器,在哪个算法中我们传递给程序的实际是中缀表达式,也就是我们最常见的表达式形式——操作符位于两个被操作数的中间,那么我们的前缀表达式、后缀表达式就是我们的操作符位于被操作数的之前、之后。前缀表达式、后缀表达式除过他们的操作符的位置有些不同外,就是在我们进行处理计算的时候前缀表达式是从右向左开始的但是我们的后缀表达式是从左向右开始的。他们操作的大致思路就都是,遇到操作数的话就将数入操作数栈,遇到操作符的时候从操作数栈中弹出两个操作数用这个操作符进行运算,并将操作结果重新的压入到操作数栈中。
上面是我们对几种表达式的一个简单介绍,我们可以看出前缀表达式、后缀表达式对计算机来说都是简单的,但是我们如何将适合我们人看的中缀表达式转换为后缀表达式呢?他的实现思路是什么呢?我们就拿中缀表达式转后缀表达式来做一个分析:
- 后缀表达式中我们的操作符对他前面的两个操作数是有操作权的,如何保证这一步呢?
- 对于一个操作数在我们不考虑小数点、括号的情况下他的左右两边一定是有两个操作符,应该是优先级高的操作符对这个数有操作权,结合我们栈的结构,以及后缀表达式运算时从做往右的特点,我们可以知道我们需要把优先级高的操作符放在操作符栈的栈顶。这样可以保证我们在出栈的时候在优先级高的操作符可以离我们这个操作数更近,对他有更高的操作权限
- 括号 并不是操作符,只不过是他可以改变操作的运算先后顺序,在括号内的元素运算符在小括号内他们的是一个小的作用域在我们这个作用域将要结束的时候我们需要这这连个括号中的运算符放出去,因为他们对这一范围内对操作数的优先级要高于这一作用域外的操作符!
public class PolandNotation {
public static void main(String[] args) {
/**
*@description: 将给出的中缀表达式转换为后缀表达式并对后缀表达式进行计算
*/
String inputString = "1+((2+3)*4)-5";
PolandCalculate(toSuffix(stringToList(inputString)));
}
public static int PolandCalculate(List<String> polandListString){
/**
*@description: 中间的一些步骤通过调用相关的函数来实现
*@params: 中缀表达式的字符串表达形式
*@returns: 表达式的结果
*/
Stack<String> numStack = new Stack<>();
Stack<String> operaStck = new Stack<>();
int num1 = 0,num2 =0;
int index = 0;
while(index<polandListString.size()){
if(isOper(polandListString.get(index))){//如果是操作符,则弹出两个数进行运算
num2 = Integer.parseInt(numStack.pop());
num1 = Integer.parseInt(numStack.pop());
numStack.push(cal(num1,num2,polandListString.get(index))+"");
}else {
numStack.push(polandListString.get(index));
}
index++;
}
System.out.println("expression = "+numStack.peek());
return Integer.parseInt(numStack.pop());
}
public static int cal(int num1, int num2, String oper) {
int res = 0; // res 用于存放计算的结果
switch (oper) {
case "+":
res = num1 + num2;
break;
case "-":
res = num2 - num1;// 注意顺序
break;
case "*":
res = num1 * num2;
break;
case "/":
res = num1 / num2;
break;
default:
break;
}
return res;
}
public static List<String> stringToList(String inputString){
/**
*@params: 中缀表达式字符串表达形式
*@returns: 中缀表达式List集合表达形式
*/
//定义一个List,存放中缀表达式 对应的内容
List<String> ls = new ArrayList<String>();
int i = 0; //这时是一个指针,用于遍历 中缀表达式字符串
String str; // 对多位数的拼接
char c; // 每遍历到一个字符,就放入到c
do {
//如果c是一个非数字,我需要加入到ls
if((c=inputString.charAt(i)) < 48 || (c=inputString.charAt(i)) > 57) {
ls.add("" + c);
i++; //i需要后移
} else { //如果是一个数,需要考虑多位数
str = ""; //先将str 置成"" '0'[48]->'9'[57]
while(i < inputString.length() && (c=inputString.charAt(i)) >= 48 && (c=inputString.charAt(i)) <= 57) {
str += c;//拼接
i++;
}
ls.add(str);
}
}while(i < inputString.length());
System.out.println("temp:"+ls.toString());
return ls;
}
public static List<String> toSuffix(List<String> temp){
//用来存放操作数
Stack<String> operStack = new Stack<>();
//用来存放中间结果
List<String> resultBridge = new ArrayList<>();
int index =0;
String tempCh;
while (index<temp.size()){
tempCh = temp.get(index);
if(isOper(tempCh)){//如果是操作符
if(tempCh.equals("(")){//左括号直接入栈
operStack.push(tempCh);
}else if(tempCh.equals(")")){//有括号,一次弹出前面栈顶的元素
while(!operStack.peek().equals("(")){
resultBridge.add(operStack.pop());
}
operStack.pop();
}else{//如果是四则运算操作符
if(operStack.size()==0){//如果当前操作符栈为空
operStack.push(tempCh);
}else{//当前操作符栈不为空
if(operPriotity(tempCh)>operPriotity(operStack.peek())){//当前操作符优先级更高
operStack.push(tempCh);
}else{
while(operStack.size()>0&&!operStack.peek().equals("(")&&
operPriotity(tempCh)<=operPriotity(operStack.peek())){
resultBridge.add(operStack.pop());
}
operStack.push(tempCh);
}
}
}
}else{//如果是数字直接入resultBridge
resultBridge.add(tempCh);
}
index++;
}
while (operStack.size()>0){
resultBridge.add(operStack.pop());
}
System.out.println("result"+resultBridge.toString());
return resultBridge;
}
public static int operPriotity(String oper){
/**
*@description: 判断运算符的优先级
*/
return oper=="*"||oper=="/"?2:1;
}
public static boolean isOper(String tempCh){
return tempCh.equals("*")||tempCh.equals("/")||tempCh.equals("+")||tempCh.equals("-")||tempCh.equals("(")||tempCh.equals(")")?true:false;
}
}