使用栈将中缀表达式转为后缀表达式并计算
一、中缀表达式转换为后缀表达式
由于后缀表达式适合计算式进行计算,但是人对于较长的中缀表达式,很难将中缀表达式直接转换为后缀表达式,于是我们使用栈来实现中缀表达式转成后缀表达式。
二、 中缀表达式转后缀表达式的分析步骤
1.初始化两个栈:运算符栈S1和存储中间结果的栈S2
2.从左到右扫描中缀表达式
3.遇到操作数时,将其压入S2中
4.遇到运算符时,比较其与S1栈顶符号的优先级
4.1)如果S1为空或者栈顶运算符为“(”,则直接将此运算符压入栈中
4.2)否则,若优先级比栈顶元素的优先级高,也将运算符压入栈S1中
4.3)否则,若优先级小于等于栈顶元素的优先级,将S1栈顶的运算符弹出,并压入S2中,再转到4.1继续与S1中新的栈顶元素相比较
5.遇到括号时:
5.1)如果是左括号,就直接压入栈S1中
5.2)如果是右括号,则牙齿弹出S1栈顶的运算符,并压入栈S2中,直到遇到左括号为止,此时这一对括号丢弃
6.重复步骤2到5,直到表达式的最右端
7.将栈S1中剩余的运算符依次弹出并压入栈S2中
8.依次弹出S2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
三、举例说明
将中缀表达式“1+((2+3)×4)-5”转换为后缀表达式的过程如下
因此结果为 : "1 2 3 + 4 × + 5 –"
四、代码实现
package cn.zzw.algorithm.Stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotion1 {
public static void main(String[] args) {
//输入中缀表达式
String expression= "1+((2+3)*4)-5";
List<String> infixExpressionList=toInfixExpressionList(expression);
System.out.println("中缀表达式对应的list为:"+infixExpressionList);
List<String> suffixExpressionList=parseSuffixExpressionList(infixExpressionList);
System.out.println("后缀表达式:"+suffixExpressionList);
int result=calculate(suffixExpressionList);
System.out.println("最终的结果为:"+result);
}
//将中缀表达式对应的List转换为后缀表达式的List
public static List<String> parseSuffixExpressionList(List<String> list)
{
//定义两个栈
Stack<String> stack1=new Stack<>();//符号栈
//说明:因为 s2 这个栈,在整个转换过程中,没有 pop 操作,而且后面我们还需要逆序输出
// 因此比较麻烦,这里我们就不用 Stack<String> 直接使用 List<String> s2
// Stack<String> stack2 = new Stack<String>(); // 储存中间结果的栈 s2
List<String> list2=new ArrayList<>();
//遍历形参传入的list集合
for(String item:list)
{
//如果是一个数,就直接加入list2
if(item.matches("\\d+"))
{
list2.add(item);
}
else if (item.equals("("))
{
stack1.push(item);
}
else if(item.equals(")"))
{
//如果是右括号“)”,则依次弹出 s1 栈顶的运算符,并压入 s2,直到遇到左括号为止,此时将这 一对括号丢弃
while (!stack1.peek().equals("("))
{
list2.add(stack1.pop());
}
//将小括号从stack1中弹出,消除小括号
stack1.pop();
}
else
{
//当 item 的优先级小于等于 s1 栈顶运算符, 将 s1 栈顶的运算符弹出并加入到 s2 中,再次转到(4.1) 与 s1 中新的栈顶运算符相比较
while (stack1.size()!=0&&Operation.getValue(item)<=Operation.getValue(stack1.peek()))
{
list2.add(stack1.pop());
}
//之后还需要将item入栈
stack1.push(item);
}
}
//最后将stack1中剩余的元素依次弹出并加入list2中
while (stack1.size()!=0)
{
list2.add(stack1.pop());
}
return list2;
}
//将中缀表达式转为对应的List
public static List<String> toInfixExpressionList(String s)
{
List<String> list=new ArrayList<>();
//定义一个指针,用于遍历中缀表达式中的字符串
int index=0;
String str;//用于字符串的拼接
char c;
do {
//如果是一个非数字,需要加入到list中
if ((c=s.charAt(index))<48||(c=s.charAt(index))>57)
{
list.add(c+"");
index++;//指针后移
}
else
{
//如果是数字,则要考虑多位数的情况
str="";//先将str置空
while (index<s.length()&&(c=s.charAt(index))>=48&&(c=s.charAt(index))<=57)
{
str+=c;
index++;
}
list.add(str);
}
}while (index<s.length());
return list;
}
//使用逆波兰表达式的运算
public static int calculate(List<String> list)
{
//创建一个占空间
Stack<String> stack=new Stack<>();
//遍历集合
for(String item:list)
{
//这里使用正则表达式取出数字
//匹配的是多位数
if(item.matches("\\d+"))
{
stack.push(item);
}
else
{
int num1=Integer.parseInt(stack.pop());
int num2=Integer.parseInt(stack.pop());
int result=0;
if(item.equals("+"))
{
result=num1+num2;
}
else if(item.equals("-"))
{
result=num2-num1;
}
else if(item.equals("*"))
{
result=num1*num2;
}
else if (item.equals("/"))
{
result=num2/num1;
}
else
{
throw new RuntimeException(("运算符有误"));
}
//重要,最后别忘了把计算出来的结果入栈
stack.push(result+"");
}
}
//最后留在栈中的数据就是最终的结果
return Integer.parseInt(stack.pop());
}
}
//编写一个类,可以返回一个运算符对应的优先级
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 getValue(String operation)
{
int result=0;
switch (operation)
{
case "+":
result=ADD;
break;
case "-":
result=SUB;
break;
case "*":
result=MUL;
break;
case "/":
result=DIV;
break;
}
return result;
}
}
五、测试结果
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" "-javaagent:D:\IntelliJ IDEA\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=21098:D:\IntelliJ IDEA\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;C:\Users\1\IdeaProjects\algorithm\out\production\algorithm" cn.zzw.algorithm.Stack.PolandNotion1
中缀表达式对应的list为:[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
后缀表达式:[1, 2, 3, +, 4, *, +, 5, -]
最终的结果为:16
Process finished with exit code 0