首先,逆波兰表达式又叫做后缀表达式
逆波兰算法的核心步骤就2个:
1、将中缀表达式转换为后缀表达式,
2、根据后缀表达式,按照特定的计算规则得到最终计算结果
步骤一思路:
中缀表达式转后缀表达式:
- 1 初始化两个栈:运算符栈s1 和储存中间结果的栈s2
- 2 从左至右扫描中缀表达式
- 3 遇到操作数时,将其压s2
- 4 遇到运算符时 比较其与s1栈顶运算符的优先级;
- (1) 如果s1 为空 ,或者栈顶运算符为左括号"(",则直接将此运算符入栈
- (2) 否则,若优先级比栈顶运算符的高, 也将运算符压入s1
- (3) 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-(1))与s1中新的栈顶运算符相比较
- 5 遇到括号时:
- (1) 如果是左括号"(",则直接压入s1
- (2) 如果是右括号")",则一次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一括号丢弃
- 6 重复步骤2至5,直到表达式的最右边
- 7 将s1中剩余的运算符依次弹出并压入s2
- 8 依次弹出s2中的元素并输出。结构的逆序即为中缀表达式对应的后缀表达式
有了思路代码如下
package com.xu.stack;
import javax.security.auth.Subject;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class infixConvertedToSuffix {
public static void main(String[] args) {
//完成一个中缀表达式转成后缀表达式的代码
//说明
//1. +=> 1 2 3 + 4 * + 5 -
//2.因为直接对str 进行操作 不方便,因此先将" 1+((2+3)*4)-5" 转成 中缀表达式对应的list
// 即" 1+((2+3)*4)-5" => ArrayList[ 1,+,(,(,2,+,3,),*,4,),-,5,]
//3.将得到的一个中缀表达式对应的List =>转成后缀表达式对应的List
//即ArrayList[ 1,+,(,(,2,+,3,),*,4,),-,5,] =>ArrayList[ 1,2,3,+,4,*,+,5,-]
String expression="1+((2+3)*4)-5";
List<String>infixExpressionList =toInfixExpressionList(expression);
System.out.println(infixExpressionList);
System.out.println(parseSuffixExpressionList(infixExpressionList));
}
//方法 将中缀表达式转成对应的List
public static List<String> toInfixExpressionList(String s){
//定义一个List,存放中缀表达式对应的内容
List<String> ls=new ArrayList<String>();
int i=0;//这个相当于一个指针 用于遍历 中缀表达式字符串
String str; //这个对多位数的一个拼接工作
char c; //每遍历到一个字符 就放入到c
do{
//如果c是一个非数字 我需要加入到ls
if ((c=s.charAt(i))<48||(c=s.charAt(i))>57){ //asc2码表48到57 表示0到9
ls.add(""+c);
i++;//i 需要后移
}else {
//如果是一个数 需要考虑多位数的问题
str="";//先将str 制空
while (i<s.length()&&(c=s.charAt(i))>=48&& (c=s.charAt(i))<=57){ //将i指针后移 并判断是否为数字
str+=c;//拼接
i++;
}
ls.add(str);
}
}while (i<s.length());
return ls;
}
//方法:将得到的一个中缀表达式对应的List =>转成后缀表达式对应的List
public static List<String> parseSuffixExpressionList(List<String>ls){
//定义两个栈
Stack<String> s1=new Stack<String>(); //符号栈
//说明:因为s2这个栈 在整个转换过程中 没有pop操作,而且后面我们还需要逆序操作
//因此比较麻烦。这里我们就不用栈 而直接用List
//Stack<String> s2=new Stack<String>(); //储存中间结果的栈
List<String>s2 =new ArrayList<String>();//储存中间结果
//遍历ls
for (String item:ls) {
//如果是一个数,就加入s2
if (item.matches("\\d+")){
s2.add(item);
}else if (item.equals("(")){
s1.push(item);
}else if (item.equals(")")){
// 如果是右括号")",则一次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一括号丢弃
while (!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop();// 将(弹出 s1栈 消除小括号
}else {
//当 item的优先级小于等于s1栈顶运算符,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-(1))与s1中新的栈顶运算符相比较
//问题 缺少一个比较优先级高低的方法
while (s1.size()!=0&&Operation.getValue(s1.peek())>=Operation.getValue(item)){
s2.add(s1.pop());
}
//还需要将item压入栈
s1.push(item);
}
}
//将s1剩余的运算符依次弹出并加入s2
while (s1.size()!=0){
s2.add(s1.pop());
}
return s2;
}
}
//编写一个类 Operation 可以返回一个运算符对应的优先级
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;
default:
System.out.println("不存在改运算符");
break;
}
return result;
}
}
代码运行结果
将得到的逆波兰表达式取出 也就是 :1, 2, 3, +, 4, *, +, 5, -
完成逆波兰表达式运算思路:
- 从左至右扫描,将1,2和3压入堆栈
- 遇到+运算符 ,因此弹出3和2 (3为栈顶元素,2为次顶元素),计算出3+2的值 ,得5.再将5入栈;
- 遇到4 再将4入栈
- 遇到运算符 将4 和5出战计算输45的值 得20,将20入栈 此时栈内只有1 和20
- 遇到 + 号之后 1 和20 出栈相加 得21
- 继续向右扫描遇到 将5入栈 遇到 -运算符 弹出21 和5 计算21-5得16就为最终结果
代码实现如下
package com.xu.stack;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.Stack;
public class PolandNotation {
public static void main(String[] args) {
String suffixExpression="1 2 3 + 4 * + 5 -";
List<String>rpnList=getListString(suffixExpression);
System.out.println("rpnList="+rpnList);
int res=calculate(rpnList);
System.out.println(res);
}
//将一个逆波兰表达式,依次将数据和运算符 放入到ArrayList中
public static List<String> getListString(String suffixExpression){
//将 suffixExpression 分割
String[] split =suffixExpression.split(" ");
List<String> list =new ArrayList<String>();
for (String ele:split) {
list.add(ele);
}
return list;
}
public static int calculate(List<String>ls){
//创建给栈,只需要一个栈即可0
Stack<String> stack=new Stack<String>();
//遍历 ls
for (String item:ls){
//这里使用正则表达式来去除数
if (item.matches("\\d+")){
//匹配的是多位数
//入栈
stack.push(item);
}else {
//pop 出两个数 ,并运算,再入栈
int num2 =Integer.parseInt(stack.pop());
int num1 =Integer.parseInt(stack.pop());
int res =0;
if(item.equals("+")){
res=num1+num2;
}else if (item.equals("*")){
res=num1*num2;
}else if (item.equals("/")){
res=num1/num2;
} else if (item.equals("-")){
res=num1-num2;
}else {
throw new RuntimeException("运算符有误");
}
//把res入栈
stack.push(res+"");
}
}
//最后留在stack中的数据就是运算结果
return Integer.parseInt(stack.pop());
}
}
运行结果图片如下: