逆波兰表达式
又称后缀表达式,将运算符写在操作数之后,便于计算机计算
- 将中缀表达式转换成后缀表达式的8个步骤:
-
初始化两个栈,运算符栈s1和存储中间结果的栈s2
-
从左至右扫描中缀表达式
-
遇到操作数时,将其压入s2
-
遇到运算符时,将其与s1栈顶运算符的优先级进行比较:
- 如果s1为空,或栈顶运算符为左括号"(",则直接将此运算符入栈
- 否则,若优先级比栈顶运算符的高,也将运算符压入s1
- 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4.1)与s1中的栈顶运算符相比较
-
遇到括号时:
- 如果是左括号"(",则直接压入s1
- 如果是右括号")",则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
-
重复步骤2至5,知道表达式最右边
-
将s1中剩余的运算符以此弹出并压入s2
-
依次弹出s2中的元素并输入,结果的逆序即为中缀表达式对应的后缀表达式。
- 1+(2+3)*4 -5 的转换步骤
代码实现
/**
* 转换成后缀表达式List
*
* @param ls 中缀表达式List
* @return
*/
public static List<String> parseSuffixExpression(List<String> ls) {
Stack<String> s1 = new Stack<>();//运算符栈
Queue<String> s2 = new ArrayDeque<>();//临时队列
for (String item : ls) {
if (item.matches("\\d+")) {
//3.如果是数字,直接入队s2
s2.offer(item);
} else if (isOper(item)) {
//4.如果是运算符
while (true) {
if (s1.isEmpty() || "(".equals(s1.peek())) {
//4.1如果运算符栈s1是空的,或栈顶是左括号,则直接入栈
s1.push(item);
break;
} else if (priority(item) > priority(s1.peek())) {
//4.2否则,若运算符优先级比栈顶的高,也入栈
s1.push(item);
break;
} else {
//4.3 否则,直接把栈顶的元素入队,继续转到4.1与新的栈顶元素比较
s2.offer(s1.pop());
}
}
} else if ("(".equals(item)) {
//5.1如果遇到左括号( ,直接压入s1
s1.push(item);
} else if (")".equals(item)) {
//5.2如果是右括号
while (true) {
String top = s1.pop();//弹出栈顶元素
if ("(".equals(top)) {
//碰到左括号,丢弃这对括号
break;
} else {
//否则入队
s2.offer(top);
}
}
} else {
throw new RuntimeException("无法识别的字符" + item + ",表达式不正确");
}
}
while (!s1.isEmpty()) {
//7.将s1剩余的表达式依次弹出并入队
s2.offer(s1.pop());
}
List<String> res = new ArrayList<>();
while (!s2.isEmpty()) {
res.add(s2.poll());
}
return res;
}
后缀表达式的计算规则:从左到右遍历表达式的每个数字和符号,遇到是数字就进栈,遇到是符号,就将处于栈顶两个数字出栈,进行运算,运算结果进栈,一直到最终获得结果。
/**
* 计算后缀表达式List,返回结果
*
* @param ls
* @return
*/
public static int calculate(List<String> ls) {
Stack<String> stack = new Stack<>();
for (String item : ls) {
if (item.matches("\\d+")) {
//如果是数字则入栈
stack.push(item);
} else {
//如果不是数字,则弹出栈顶和次栈顶元素进行计算
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if ("+".equals(item)) {
res = num1 + num2;
} else if ("-".equals(item)) {
res = num1 - num2;
} else if ("*".equals(item)) {
res = num1 * num2;
} else if ("/".equals(item)) {
res = num1 / num2;
} else {
throw new RuntimeException("运算符有误");
}
//计算过的结果再次入栈
stack.push("" + res);
}
}
//栈中的结果就是表达是的结果,直接返回
return Integer.parseInt(stack.pop());
}
简单转换(括号法)
1.按照运算符的优先级对所有的运算单位加括号~
2.把运算符号移动到对应的括号后面
3.去除所有括号
例:
- 1+2
- (1+2)
- (1 2)+
- 1 2 +
- 1+2×3
- (1+(2 × 3))
- (1+(2 3)×)
- (1 (2 3 ×) ) +
- 1 2 3 × +
- (2-1)×3+15÷(1+4)
- (((2 - 1) × 3) + (15 ÷ (1+4)))
- (((2 1) - × 3) + (15 ÷ (1 4)+))
- (((2 1) - 3) × + (15 (1 4)+) ÷ )
- (((2 1) - 3) × (15 (1 4)+) ÷ ) +
- 2 1 - 3 × 15 1 4 + ÷ +
//完整java代码
import java.util.*;
/**
* 逆波兰表达式(后缀表达式)
* 支持整数、浮点 + - * / ( )运算符,可自动过滤非法字符
*/
public class PolandNotation {
public static void main(String[] args) {
String expression = "(2.1-1)* 3.1+ 15/(2+4)";
System.out.println("中缀表达式:" + expression);
List<String> list = toInfixExpressionList(expression);
System.out.println("中缀表达式List:" + list);
List<String> suffixExpression = parseSuffixExpression(list);
System.out.println("后缀表达式List:" + suffixExpression);
float calculate = calculate(suffixExpression);
System.out.println("计算结果:" + calculate);
}
/**
* 转换成后缀表达式List
*
* @param ls 中缀表达式List
* @return
*/
public static List<String> parseSuffixExpression(List<String> ls) {
Stack<String> s1 = new Stack<>();//运算符栈
Queue<String> s2 = new ArrayDeque<>();//临时队列
for (String item : ls) {
if (item.matches(REGEX_FLOAT)) {
//3.如果是数字,直接入队s2
s2.offer(item);
} else if (isOper(item)) {
//4.如果是运算符
while (true) {
if (s1.isEmpty() || "(".equals(s1.peek())) {
//4.1如果运算符栈s1是空的,或栈顶是左括号,则直接入栈
s1.push(item);
break;
} else if (priority(item) > priority(s1.peek())) {
//4.2否则,若运算符优先级比栈顶的高,也入栈
s1.push(item);
break;
} else {
//4.3 否则,直接把栈顶的元素入队,继续转到4.1与新的栈顶元素比较
s2.offer(s1.pop());
}
}
} else if ("(".equals(item)) {
//5.1如果遇到左括号( ,直接压入s1
s1.push(item);
} else if (")".equals(item)) {
//5.2如果是右括号
while (true) {
String top = s1.pop();//弹出栈顶元素
if ("(".equals(top)) {
//碰到左括号,丢弃这对括号
break;
} else {
//否则入队
s2.offer(top);
}
}
} else {
throw new RuntimeException("无法识别的字符" + item + ",表达式不正确");
}
}
while (!s1.isEmpty()) {
//7.将s1剩余的表达式依次弹出并入队
s2.offer(s1.pop());
}
List<String> res = new ArrayList<>();
while (!s2.isEmpty()) {
res.add(s2.poll());
}
return res;
}
/**
* 是否操作符
*
* @param val 元素
* @return
*/
public static boolean isOper(String val) {
return "+".equals(val) || "-".equals(val) || "*".equals(val) || "/".equals(val);
}
/**
* 将中缀表达式转换成对应的List
*
* @param s 中缀表达式字符串
* @return
*/
public static List<String> toInfixExpressionList(String s) {
List<String> ls = new ArrayList<>();
int i = 0;//指针,用于遍历中缀表达式字符串
String str;//对多位数的拼接工作
char c;//每遍历到一个字符,放入c
do {
c = s.charAt(i);//获取元素
if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')') {
//如果元素是运算符直接添加到List
ls.add(c + "");
i++;
} else if (Character.isDigit(c) || c == '.') {
//如果元素是数字和小数点
str = "";
while (i < s.length() && (Character.isDigit(s.charAt(i)) || s.charAt(i) == '.')) {//直至下个不是小数点或数字的位置
str += "" + s.charAt(i++);
}
ls.add(str);
} else {
i++;
}
} while (i < s.length());
return ls;
}
/**
* 返回操作符优先级
*
* @param operString 操作符
* @return
*/
public static int priority(String operString) {
if ("*".equals(operString) || "/".equals(operString)) {
return 1;
} else if ("+".equals(operString) || "-".equals(operString)) {
return 0;
} else {
return -1;
}
}
/**
* 数字正则
*/
private final static String REGEX_FLOAT = "\\d+||\\d*\\.\\d+||\\d*\\.?\\d+?e[+-]\\d*\\.?\\d+?||e[+-]\\d*\\.?\\d+?";
/**
* 计算后缀表达式List,返回结果
*
* @param ls
* @return
*/
public static float calculate(List<String> ls) {
Stack<String> stack = new Stack<>();
for (String item : ls) {
if (item.matches(REGEX_FLOAT)) {
stack.push(item);//如果是数字则入栈
} else {
//如果不是数字,则弹出栈顶和次栈顶元素进行计算
Float num2 = Float.parseFloat(stack.pop());
Float num1 = Float.parseFloat(stack.pop());
Float res = 0f;
if ("+".equals(item)) {
res = num1 + num2;
} else if ("-".equals(item)) {
res = num1 - num2;
} else if ("*".equals(item)) {
res = num1 * num2;
} else if ("/".equals(item)) {
res = num1 / num2;
} else {
throw new RuntimeException("运算符有误");
}
//计算过的结果再次入栈
stack.push("" + res);
}
}
//栈中的结果就是表达是的结果,直接返回
return Float.parseFloat(stack.pop());
}
}