最近在做一个关于计算器的小demo,其中在计算环节需要把待计算的中缀表达式转换成计算机识别的后缀表达式。所以就想系统的整理一下关于中缀表达式转换的问题。
一、介绍
关于中缀表达式转换后缀表达式的理解,可以参考《数据结构和算法》之中缀表达式、后缀表达式转换,这里面给了一个具体实例转换的过程,比较容易理解。
二、实现算法
遍历中缀表达式:
1、如果遇到数字,就输出到后缀表达式字符串数组中
2、如果是操作符(+,-,*,/):
(1)如果栈为空,直接入栈
(2)如果栈非空,且该操作符优先级大于栈顶元素,则入栈
(3)如果该操作符优先级小于栈顶元素,则弹出栈顶元素
3、如果是左括号,则直接入栈
4、如果是右括号,则依次弹出栈顶元素,直到遇到左括号为止。
5、如果中缀表达式遍历结束,则依次弹出栈顶元素
三、代码实现
分别用InfixIndex和PostfixIndex用来遍历中缀表达式和后缀表达式
PostStr[]用来存储后缀表达式
这里还需要注意一下,因为每一个数字都是一个字符,所以对于数字56来说,其实他是由字符“5”和字符“6”组成,所以要想办法把他们合并在一起,这里我设定了isMul用来判断是否有连续的数字情况
然后按照上述思想进行代码实现:
(1)如果当前字符是数字,下一个字符也是数字,则把他们连接在一起当成一个字符(如56),并把
isMul置1
(2)如果当前字符是数字,下一个字符不是数字,则有以下两种情况(参照表达式56*3+5=):
a.如果
isMul是1,例上面表达式中当前字符指向了数字6,因为前面在指向5的时候已经将56一起输出到
PostStr[]了,如果再一次将6输出,则会重复,所以此时不做任何操作,只要将
isMul置0即可
b.如果
isMul是0,则将当前字符输出到
PostStr[]中即可,例上面的数字3
(3)如果当前字符是右括号,则依次弹出栈,直到匹配到左括号为止
(4)如果当前字符是加减,若栈为空则直接入栈,否则弹出比他们优先级大的操作符
(5)如果当前字符是乘除左括号,直接入栈
(6)中缀表达式结束,依次将栈中元素弹出
private String converExpr(String expression) {
int InfixIndex = 0;
int PostfixIndex = 0;
String PostStr[] = new String[expression.length()];
PostStr[0]="";
Stack<String> stack = new Stack<String>();
//char post[] = new char[100];
String topString;//栈顶元素
String curString="";//当前元素
String NextString = "";//当前元素的下一个元素
int isMul=0;//判断是否是复数
while(!NextString.equals("=")) {
curString = String.valueOf(expression.charAt(InfixIndex));
NextString = String.valueOf(expression.charAt(InfixIndex+1));
// 如果是数字,直接添加进字符中
// 如果前下一个字符也是数字,把他们并到一起
if (curString.matches("[\\d\\.]") && NextString.matches("[\\d\\.]")) {
if(isMul==0) {
PostStr[PostfixIndex] = PostStr[PostfixIndex] + curString.concat(NextString);
Log.i("a", "PostStr[i]" + PostStr[PostfixIndex]);
}
else{
PostStr[PostfixIndex] = PostStr[PostfixIndex].concat(NextString);
Log.i("a", "PostStr[i]" + PostStr[PostfixIndex]);
}
isMul=1;
//InfixIndex++;
//PostfixIndex++;
} else if(curString.matches("[\\d\\.]")&& NextString.matches("[^\\d\\.]")&& isMul==0){
PostStr[PostfixIndex]=curString;
Log.i("a","PostStr[i]"+PostStr[PostfixIndex]);
PostfixIndex++;
} else if(curString.matches("[\\d\\.]")&& NextString.matches("[^\\d\\.]")&& isMul==1){
isMul=0;
PostfixIndex++;
PostStr[PostfixIndex]="";
} else if (curString.equals(")")) {
//如果是右括号,将栈中其他运算符出栈,添加进字符数组,知道匹配到左括号
while (!stack.peek().equals("(")) {
PostStr[PostfixIndex] = stack.peek();
PostfixIndex++;
stack.pop();
}
stack.pop();
} else if (curString.equals("+")|| curString.equals("-")) {
//如果是加减符号
if (stack.isEmpty())//空栈就直接入栈
stack.push(curString);
else {//弹出优先级高于加减的运算符(因为加减运算符优先级最低)
while (!stack.isEmpty()) {
topString = stack.peek();
stack.pop();
if (topString .equals("(")) {
stack.push(topString);
break;
} else {
PostStr[PostfixIndex] = topString;
PostfixIndex++;
}
}
stack.push(curString);
}
} else if (curString .equals("x") || curString .equals("/") || curString .equals("(")) {
//如果是乘除、左括号,优先级高,直接入栈
stack.push(curString);
}else{
return "格式错误";
}
InfixIndex++;
Log.i("ss","PostStr[i]"+PostStr[PostfixIndex]);
}
//字符遍历完后将栈中剩余的字符出栈
while (!stack.isEmpty()) {
PostStr[PostfixIndex] = stack.peek();
PostfixIndex++;
stack.pop();
}
return calcuPost(PostStr);
}
下面就是后缀表达式的计算:
依次遍历后缀表达式,遇到数字就入栈,遇到操作符就将栈顶的两个元素弹出进行运算,再把运算结果入栈,直到后缀表达式遍历结束,将栈中元素弹出即是最终计算结果
代码如下:
/**
* 根据后缀表达式计算结果
* @param post
*/
private String calcuPost(String post[]) {
LinkedList<String> mList = new LinkedList<>();
for (String s : post) {
if (!TextUtils.isEmpty(s)) {
//遇到运算符就对栈顶的两个数字运算
if (isEqualString(s)) {
if (!mList.isEmpty()) {
int num1 = Integer.valueOf(mList.pop());
int num2 = Integer.valueOf(mList.pop());
if (s.equals("/") && num1 == 0) {
return "除数不能为空";
}
mList.push(cal(num2,num1,s));
}
} else {
//遇到数字就入栈
mList.push(s);
}
}
}
if (!mList.isEmpty()) {
return mList.pop();
}
return "表达式错误";
}
private static String cal(int num2, int num1, String op) {
switch (op) {
case "+":
return String.valueOf(num1 + num2);
case "-":
return String.valueOf(num2 - num1);
case "x":
return String.valueOf(num1 * num2);
case "/":
return String.valueOf(num2*1.0 / num1);
default:
return "";
}
}