中缀表达式转换为后缀表达式
后缀表达式适合计算式进行运算,但是人却不太容易写出来,尤其是表达式很长的情况下,因此在开发中,我们需要将 中缀表达式转成后缀表达式。
1.转换具体步骤:
- 初始化两个栈:运算符栈s1和储存中间结果的栈s2
- 从左至右扫描中缀表达式
- 遇到操作数时,将其压s2
- 遇到运算符时,比较其与s1栈顶运算符的优先级:
1. 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈
2. 否则,若优先级比栈顶运算符的高,也将运算符压入s1
3. 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4.1)与s1中新的栈顶运算符相比较。 - 遇到括号时:
1. 如果是左括号“(”,则直接压入s1
2. 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃 - 重复步骤2至5,直到表达式的最右边
- 将s1中剩余的运算符依次弹出并压入s2
- 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
- 因为栈的先进后出的方式,因此结果的逆序才是后缀表达式
- 我们也可以直接用ArrayList来当作存储中间结果的s2,此时list的顺序输出即后缀表达式。
2.代码演示:
以1+((2+3)*4)-5为例演示。
- 当我们有一个中缀表达式的时候,需要先将其放在一个列表中,为了方便后续对其操作,使其转换为后缀表达式。同时,操作列表比操作str类型的数据要方便。
public static List<String> toInfixExpressionList(String s){
//1.定义一个list存放中缀表达式
ArrayList<String> list = new ArrayList<>();
int i = 0;
String ch;//对多位数的拼接
char c;//每遍历到一个数字/操作符就暂时存储在c中
do {
if ((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57){//如果是操作符
list.add("" + c);
i++;
}else {//如果是数字,需要考虑多位数的拼接
ch = "";
while (i < s.length() && ((c = s.charAt(i)) >= 48 && (c = s.charAt(i) )<= 57)){
ch += c;
i++;
}
list.add(ch);
}
}while (i<s.length());
return list;
}
- 当我们得到中缀表达式的列表后,为了转换成为后缀表达式,因为考虑到不仅有加减乘除的操作符,还有()等改变优先级的方式,具体的转换步骤可参考上文的转换具体步骤。
- 由于涉及到操作符的入栈和出栈,涉及到了运算符的优先级问题,因此,我们需要写一个方法来定义运算符的优先级。此处是直接构造了一个类,在类的内部声明了静态方法,同时可以通过类来调用该方法。
优先级的方法
class Operation{
private static int ADD = 1;
private static int SUB = 1;
private static int MUL = 2;
private static int DIV = 2;
public static int getPriority(String oper){
int res = 0;
switch (oper){
case "+":
res = ADD;
break;
case "-":
res = SUB;
break;
case "*":
res = MUL;
break;
case "/":
res = DIV;
break;
default:
System.out.println("输入错误~");
break;
}
return res;
}
}
中缀表达式转换为后缀表达式的方法
public static List<String> parseSuffixExpression(List<String> ls){
//1.定义一个栈,一个ArrayList
//因为lsit只是用来储存数据,不涉及到栈的pop功能,而且,如果使用栈的话,Pop完后缀表达式后,还需要逆序输出才是真正的后缀表达式
//没有List方便
Stack<String> s1 = new Stack<>();//操作符栈
ArrayList<String> s2 = new ArrayList<>();//存储数据与操作符
//遍历ls
for(String s:ls){
//如果指针指向的是一个数字,则直接加入s2
if (s.matches("\\d+")){
s2.add(s);
}else if("(".equals(s)){
s1.push(s);
}else if(")".equals(s)){//当遇到)括号时候,为了和(消掉,需要先将之前的那些操作符全部出栈放到s2中
while (!s1.peek().equals("(")){
s2.add(s1.pop());
}s1.pop();
}else {
//当传入的操作符优先级大于当前栈顶的操作符时候,直接入栈,否则就需要将高优先级的放到s2,直到栈顶的低于当前的优先级
//构造一个比较优先级高低的方法
while (s1.size() != 0 && (Operation.getPriority(s1.peek()) > Operation.getPriority(s))){
s2.add(s1.pop());
}s1.push(s);
}
}
//将s1中的剩下来的操作符依次放入s2中
while (s1.size() != 0){
s2.add(s1.pop());
}
return s2;
}
- 在主函数测试部分,需要先调用中缀表达式转换列表,再调用中缀表达式转换成后缀表达式的方法,然后可以根据之前写的计算的方法来得出数值,验证方法正确与否。
public class PolandNotation {
public static void main(String[] args) {
String suffixExpression = "1+((2+3)*4)-5";
List<String> stringList = toInfixExpressionList(suffixExpression);
System.out.println("中缀表达式转换成list后: " + stringList);
List<String> pSEList = parseSuffixExpression(stringList);
System.out.println("中缀转换为后缀后: " + pSEList);
int caculate = caculate(pSEList);
System.out.println("计算结果为: " + caculate);
}
结果展示
以1+((2+3)*4)-5为例演示,正确结果应该为16.
出现的两个”输入错误~“的语句是因为在写操作符优先级的时候没有考虑(),因此在调用时候直接进入了default,调用了其中的语句。
递归、排序、查找算法冲冲冲~~~