中缀表达式转化为后缀表达式
IDEA自动补全类声明:ctrl+Alt+V
后缀表达式适合计算式进行运算,但是人却不太容易写出来,尤其是表达式很长的情况下,因此在开发中,我们需要将中缀表达式转成后缀表达式。
具体步骤如下:
- 初始化两个栈:运算符栈s1和储存中间结果的栈s2;
- 从左至右扫描中缀表达式;
- 遇到操作数时,将其压s2;
- 遇到运算符时,比较其与s1栈顶运算符的优先级:
- 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
- 否则,若优先级比栈顶运算符的高,也将运算符压入s1;
- 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4_1)与s1中新的栈顶运算符相比较;
- 遇到括号时:
- 如果是左括号“(”,则直接压入s1
- 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
- 重复步骤2至5,直到表达式的最右边
- 将s1中剩余的运算符依次弹出并压入s2
- 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
例:1+((2+3)×4)-5
将中缀表达式1 + ( ( 2 + 3 ) × 4 ) - 5
转换为后缀表达式的过程如下:
- S2存放中间结果
- S1存放运算符
- S2出栈的逆序:
1 2 3 + 4 × + 5 -
扫描到的元素 | s2(栈底->栈顶) | s1 (栈底->栈顶) | 说明 |
---|---|---|---|
1 | 1 | 空 | 数字,直接入栈 |
+ | 1 | + | s1为空,运算符直接入栈 |
( | 1 | + ( | 左括号,直接入栈 |
( | 1 | + ( ( | 同上:左括号,直接入栈 |
2 | 1 2 | + ( ( | 数字 |
+ | 1 2 | + ( ( + | s1栈顶为左括号,运算符直接入栈 |
3 | 1 2 3 | + ( ( + | 数字,直接入栈 |
) | 1 2 3 + | + ( | 右括号,弹出运算符直至遇到左括号 |
× | 1 2 3 + | + ( × | s1栈顶为左括号,运算符直接入栈 |
4 | 1 2 3 + 4 | + ( × | 数字 |
) | 1 2 3 + 4 × | + | 右括号,弹出运算符直至遇到左括号 |
- | 1 2 3 + 4 × + | - | -与+优先级相同,因此弹出+,再压入- |
5 | 1 2 3 + 4 × + 5 | - | 数字 ,直接入栈 |
到达最右端 | 1 2 3 + 4 × + 5 - | 空 | s1中剩余的运算符 |
代码实现
str -> 中缀表达式
完成中缀表达式转为后缀表达式,由于直接对str进行操作不太方便,因此将"1+((2+3)*4)-5" => 中缀表达式对应的List
1+((2+3)*4)-5
=>ArrayList[1,+,(,2,+,3,),*,4,),-,5]
- 字符对应的数字:
- ‘0’-> [48] ;
- ‘9’->[57]
- 取出String中的字符,并转为char类型:
str.charAt(index)
/**
* 将中缀表达式转为对应的List
* 比如:`1+((2+3)*4)-5` => `ArrayList[1,+,(,2,+,3,),*,4,),-,5]`
*
* @param s 输入的表达式字符串:1+((2+3)*4)-5
* @return ArrayList[1,+,(,2,+,3,),*,4,),-,5]
*/
public static List<String> toInfixExpressionList(String s) {
//定义List存放中缀表达对应的内容
List<String> ls = new ArrayList<String>();
//用于遍历中缀表达式字符串的指针
int index = 0;
//多位数的拼接
String str;
//遍历到的字符
char c;
do {
//如果是一个非数字,就需要加入ls
if ((c = s.charAt(index)) < 48 || (c = s.charAt(index)) > 57) {
//转化为字符串
ls.add("" + c);
index++;
} else {
//如果是数,需要考虑多位数的问题
//先将str置成空串
str = "";
while ((index < s.length()) && ((c = s.charAt(index)) >= 48) && ((c = s.charAt(index)) <= 57)) {
//'0'-> [48] ;'9'->[57]
//拼接字符串
str += c;
index++;
}
ls.add(str);
}
} while (index < s.length());
return ls;
}
测试
String expression = "1+((2+3)*4)-5";
List<String> infixExpression = toInfixExpressionList(expression);
System.out.println(infixExpression);
//输出结果
[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
返回操作符优先级
/**
* 返回运算符优先级的类
*/
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;
}
}
中缀表达式list->后缀表达式list
将得到的中缀表达式对应的list转为后缀表达式对应的list
- 即将
ArrayList[1,+,(,2,+,3,),*,4,),-,5]
=>ArrayList[1,2,3,+,4,*,+,5,-]
/**
* 将得到的中缀表达式对应的list转为后缀表达式对应的list
*
* @param ls 中缀表达式对应的list
* @return java.util.List<java.lang.String>
*/
public static List<String> parseSuffixExpression(List<String> ls) {
//定义两个栈
//符号栈
Stack<String> s1 = new Stack<String>();
//因为S2这个栈,转换过程中没有pop操作,且还需逆序输出,可以不用Stack<String>
//直接使用List<String> s2
//Stack<String> s2 =new Stack<String> ();
//储存中间结果的List2
List<String> s2 = new ArrayList<String>();
for (String item : ls) {
if (item.matches("\\d+")) {
//如果是一个数,直接加入S2
s2.add(item);
} else if (item.equals("(")) {
//左括号也直接加入
s1.push(item);
} else if (item.equals(")")) {
//如果为有括号,则需要弹出s1中的运算符,直到遇到左括号
while (!s1.peek().equals("(")) {
s2.add(s1.pop());
}
//将左括号弹出
s1.pop();
} else {
//当item的优先级小于等于s1栈顶的运算符时,将s1栈顶的运算符弹出并加入到s2中
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());
}
//因为是存放到List,因此按顺序输出就是对应的后缀表达式对应的List
return s2;
}
测试
List<String> suffixExpressionList = parseSuffixExpression(infixExpression);
System.out.println("后缀表达式对应的List=" + suffixExpressionList);
System.out.printf("expression = %d",calculate(suffixExpressionList));
//输出结果
不存在该运算符
不存在该运算符
后缀表达式对应的List=[1, 2, 3, +, 4, *, +, 5, -]
expression = 16
完整代码
/**
* Copyright (C), 2020-2020, 人生无限公司
* FileName: PolandNotation
* Description:逆波兰表达式
*
* @create: 2020/9/7 8:34
* @author Reanon
* @version JDK 1.8.0_251
*/
package stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotation {
public static void main(String[] args) {
String expression = "1+((2+3)*4)-5";
List<String> infixExpression = toInfixExpressionList(expression);
System.out.println(infixExpression);
List<String> suffixExpressionList = parseSuffixExpression(infixExpression);
System.out.println("后缀表达式对应的List=" + suffixExpressionList);
System.out.printf("expression = %d",calculate(suffixExpressionList));
// //逆波兰表达式,使用空格隔开
// //(3+4)*5-6 => 3 4 + 5 * 6 - =>29
// //(30+4)*5-6 => 30 4 + 5 * 6 - =>164
// //4*5 - 8 + 60 +8/2 => 4 5 * 8 - 60 + 8 2 / + =>76
// String suffixExpression = "4 5 * 8 - 60 + 8 2 / +";
// /*
// 先将suffixExpression放入ArrayList中
// 将ArrayList传给一个方法,遍历ArrayList配合栈完成计算
// */
// List<String> rpnList = getListString(suffixExpression);
// System.out.println("rpnList= " + rpnList);
//
// int res = calculate(rpnList);
// System.out.println("计算的结果=" + res);
}
/**
* 将一个逆波兰表达式的数据和运算符依次放入ArrayList中
*
* @param suffixExpression 逆波兰表达式
* @return java.util.List<java.lang.String>
*/
public static List<String> getListString(String suffixExpression) {
//将suffixExpression分割,以空格作为分割的标志
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<String>();
for (String s : split) {
list.add(s);
}
return list;
}
/**
* 完成对逆波兰表达式的计算
* 1. 从左至右扫描,将3和4压入堆栈;
* 2. 遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
* 3. 将5入栈;
* 4. 接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
* 5. 将6入栈;
* 6. 最后是-运算符,计算出35-6的值,即29,由此得出最终结果
*
* @param ls
* @return int
*/
public static int calculate(List<String> ls) {
//创建栈,只需一个栈即可
Stack<String> stack = new Stack<>();
//遍历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入栈,需要把结果转为string类型,可以直接在前面加一个空串
stack.push("" + res);
}
}
//将最后的stack中的数弹出
return Integer.parseInt(stack.pop());
}
/**
* 将中缀表达式转为对应的List
* 比如:`1+((2+3)*4)-5` => `ArrayList[1,+,(,2,+,3,),*,4,),-,5]`
*
* @param s 输入的表达式字符串:1+((2+3)*4)-5
* @return ArrayList[1,+,(,2,+,3,),*,4,),-,5]
*/
public static List<String> toInfixExpressionList(String s) {
//定义List存放中缀表达对应的内容
List<String> ls = new ArrayList<String>();
//用于遍历中缀表达式字符串
int index = 0;
//多位数的拼接
String str;
//遍历到的字符
char c;
do {
//如果是一个非数字,就需要加入ls
if ((c = s.charAt(index)) < 48 || (c = s.charAt(index)) > 57) {
//转化为字符串
ls.add("" + c);
index++;
} else {
//如果是数,需要考虑多位数的问题
//先将str置成空串
str = "";
while ((index < s.length()) && ((c = s.charAt(index)) >= 48) && ((c = s.charAt(index)) <= 57)) {
//'0'-> [48] ;'9'->[57]
//拼接字符串
str += c;
index++;
}
ls.add(str);
}
} while (index < s.length());
return ls;
}
/**
* 将得到的中缀表达式对应的list转为后缀表达式对应的list
*
* @param ls 中缀表达式对应的list
* @return java.util.List<java.lang.String>
*/
public static List<String> parseSuffixExpression(List<String> ls) {
//定义两个栈
//符号栈
Stack<String> s1 = new Stack<String>();
//因为S2这个栈,转换过程中没有pop操作,且还需逆序输出,可以不用Stack<String>
//直接使用List<String> s2
//Stack<String> s2 =new Stack<String> ();
//储存中间结果的List2
List<String> s2 = new ArrayList<String>();
for (String item : ls) {
if (item.matches("\\d+")) {
//如果是一个数,直接加入S2
s2.add(item);
} else if (item.equals("(")) {
//左括号也直接加入
s1.push(item);
} else if (item.equals(")")) {
//如果为有括号,则需要弹出s1中的运算符,直到遇到左括号
while (!s1.peek().equals("(")) {
s2.add(s1.pop());
}
//将左括号弹出
s1.pop();
} else {
//当item的优先级小于等于s1栈顶的运算符时,将s1栈顶的运算符弹出并加入到s2中
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());
}
//因为是存放到List,因此按顺序输出就是对应的后缀表达式对应的List
return s2;
}
}
/**
* 返回运算符优先级的类
*/
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;
}
}