前言
我们知道,人和计算机不一样,(1+((2+3)*4)-4)/2,这样一个表达式,对我们来说很好计算,我们直接先算括号里面的,然后在一步一步计算,对于计算机来说,他们不行,因为他们是从一端到另一端挨个扫描的,他们不是去跳着扫描的括号,先计算括号里的值,所以对于计算机来说,他们适合计算后缀表达式,今天我们就使用java来实现传入一个算术表达式,我们将其转换为后缀表达式,并计算出结果的程序。
如何将中缀算术表达式转换为后缀表达式?
第一步:将中缀表达式转换成List
原理分析:
1.我们首先进行对整个表达式进行遍历
2.当遍历到的是数字的时候(matches(“\d+”)是用来匹配是否是数字的正则表达式,是就返回true,否就返回false),此时我们不能直接将数字放入list,因为有可能是多位数,此时你只扫描了第一位数,所以我们往后面看一位
3.使用while循环,在i小于表达式长度且下一位还是数字的时候,将这些数字进行拼接,同时i++,否则外层遍历表达式的for循环会乱,然后还是循环,直至后面一位不是数字时,将拼接的数字放入list中,记得将拼接字符串初始化str=“”,
4.如果不是数字,那么就是个运算符了,我们直接放入list中
代码如下:
//将中缀表达式转成对应的List
//s="1+((2+3)*4)-5"
public static List<String> toInfixExpressionList(String s) {
//用于多位数拼接
String str = "";
List<String> ls = new ArrayList<>();
//遍历字符串s
for (int i = 0; i < s.length(); i++) {
//如果为数字
if (s.substring(i, i + 1).matches("\\d+")) {
str = str + s.substring(i, i + 1);
//循环往后看一位是不是数字,直到非数字,每次都需要i++
while (i < s.length() - 1 && s.substring(i + 1, i + 2).matches("\\d+")) {
str = str + s.substring(i + 1, i + 2);
i++;
}
//然后把拼接的多位数放入list中,并把str置空,避免下次出现混乱
ls.add(str);
str = "";
} else {
ls.add(s.substring(i, i + 1));
}
}
return ls;
}
第二步:写一个方法,返回对应运算符的优先级
因为在中缀表达式转后缀表达式的时候需要比较运算符的优先级,所以这里我们写一个方法进行比较。
这里很简单,我们只需要返回一个数字即可,+ - 我们定义为1,* / 我们定义为2,其他的不考虑,然后返回对应优先级数字即可。
代码如下:
//写一个方法,返回对应的优先级数字
public static int getValue(String operation) {
int result = 0;
switch (operation) {
case "+":
result = 1;
break;
case "-":
result = 1;
break;
case "*":
result = 2;
break;
case "/":
result = 2;
break;
default:
throw new RuntimeException("这不像是一个运算符哦!");
}
return result;
}
第三步:将中缀表达式list转成对应的后缀表达式list
原理分析:
1、初始化一个栈和一个list分别用来存放符号和后缀表达式
2、从左至右扫描中缀表达式
3、遇到操作数时,将其压入list
4、遇到运算符时,比较其与栈顶运算符的优先级
4.1、如果栈为空,或者栈顶运算符为左括号"(“,则直接将此运算符入栈
4.2、否则,若优先级比栈顶运算符的高,也将运算符直接压入栈
4.3、否则,将栈顶的运算符弹出并放入list中,再次转到4.1、与栈中新的栈顶运算符进行比较
5、遇到括号时
5.1、如果是左括号”(“,则直接压入栈
5.2、如果是右括号”)",则依次弹出栈顶的运算符,并放入list,直到遇到左括号为止,此时将这一对括号丢弃
6、重复2至5,直到表达式的最右边
7、将栈中剩余的运算符依次弹出并放入list
8、此时list中就是后缀表达式的list集合
代码如下:
//方法:将得到的中缀表达式对应的list 转成 后缀表达式对应的List
public static List<String> parseSuffixExpressionList(List<String> ls) {
//定义一个栈用来存放符号,再定义一个list用来存放后缀表达式
Stack<String> s1 = new Stack<>();
List<String> s2 = new ArrayList<>();
//遍历ls,扫描中缀表达式
for (String item : ls) {
//遇到操作数,直接压入栈
if (item.matches("\\d+")) {
s2.add(item);
} else if (item.equals("(")) {//如果是“(”,则直接压入s1
s1.push(item);
} else if (item.equals(")")) {//如果是“)”则依次弹出s1栈顶运算符,并压入s2,直到遇到"("为止,此时这一对括号丢弃
while (true) {
if (s1.peek().equals("(")) {
s1.pop();
break;
} else {
s2.add(s1.pop());
}
}
} else {//遇到运算符,需要与s1栈顶运算符比较
while (true) {
if (s1.isEmpty() || s1.peek().equals("(")) {//为空 或者 栈顶为“(” 直接放入
s1.push(item);
break;
} else if (getValue(item) > getValue(s1.peek())) {//如果优先级比栈顶运算符的高,也将运算符压入s1
s1.push(item);
break;
} else if (getValue(item) <= getValue(s1.peek())) {//优先级小于等于的时候将栈顶放入s2,然后在循环比较运算符
s2.add(s1.pop());
}
}
}
}
while (!s1.isEmpty()){
s2.add(s1.pop());
}
return s2;
}
第三步:将后缀表达式进行计算
原理分析:
从左至右扫面后缀表达式,遇到数字时,将数字压入栈,遇到运算符的时候,弹出栈顶的两个数,用运算符对他们做相应的计算,并将结果入栈:重复上述过程直到表达式最右端,最后运算出的值即为表达式的结果。(注意:栈是先进后出,所以在做-或/运算时,需要拿第二个弹出的运算符去- / 第一个)
代码如下:
//计算的方法
public static double calculate(List<String> ls) {
//创建一个栈,只需要一个栈即可
Stack<String> stack = new Stack<String>();
//遍历ls
for (String item : ls) {
//匹配的是多位数
if (item.trim().matches("\\d+")) {
//入栈
stack.push(item.trim());
} else {
//pop出两个数,并运算,再入栈
double num1 = Double.parseDouble(stack.pop());
double num2 = Double.parseDouble(stack.pop());
double res = 0;
if (item.trim().equals("+")) {
res = num1 + num2;
} else if (item.trim().equals("-")) {
res = num2 - num1;
} else if (item.trim().equals("*")) {
res = num1 * num2;
} else if (item.trim().equals("/")) {
res = num2 / num1;
} else {
throw new RuntimeException("运算符有错误");
}
//把res 入栈
stack.push("" + res);
}
}
//最后留在stack中的数据是运算结果
return Double.parseDouble(stack.pop());
}
运算结果如下:
(1+((2+3)*4)-4)/2=8.5
源码
package com.haot.stack;
import java.util.*;
public class PolandNotation {
public static void main(String[] args) {
//定义一个算数表达式
String s1 = "(1+((2+3)*4)-4)/2";
List<String> infixExpressionList = toInfixExpressionList(s1);
List<String> suffixExpressionList = parseSuffixExpressionList(infixExpressionList);
double calculate = calculate(suffixExpressionList);
System.out.println(s1+"="+calculate);
}
//将中缀表达式转成对应的List
//s="1+((2+3)*4)-5"
public static List<String> toInfixExpressionList(String s) {
//用于多位数拼接
String str = "";
List<String> ls = new ArrayList<>();
//遍历字符串s
for (int i = 0; i < s.length(); i++) {
//如果为数字
if (s.substring(i, i + 1).matches("\\d+")) {
str = str + s.substring(i, i + 1);
//循环往后看一位是不是数字,直到非数字,每次都需要i++
while (i < s.length() - 1 && s.substring(i + 1, i + 2).matches("\\d+")) {
str = str + s.substring(i + 1, i + 2);
i++;
}
//然后把拼接的多位数放入list中,并把str置空,避免下次出现混乱
ls.add(str);
str = "";
} else {
ls.add(s.substring(i, i + 1));
}
}
return ls;
}
//方法:将得到的中缀表达式对应的list 转成 后缀表达式对应的List
public static List<String> parseSuffixExpressionList(List<String> ls) {
//定义一个栈用来存放符号,再定义一个list用来存放后缀表达式
Stack<String> s1 = new Stack<>();
List<String> s2 = new ArrayList<>();
//遍历ls,扫描中缀表达式
for (String item : ls) {
//遇到操作数,直接压入栈
if (item.matches("\\d+")) {
s2.add(item);
} else if (item.equals("(")) {//如果是“(”,则直接压入s1
s1.push(item);
} else if (item.equals(")")) {//如果是“)”则依次弹出s1栈顶运算符,并压入s2,直到遇到"("为止,此时这一对括号丢弃
while (true) {
if (s1.peek().equals("(")) {
s1.pop();
break;
} else {
s2.add(s1.pop());
}
}
} else {//遇到运算符,需要与s1栈顶运算符比较
while (true) {
if (s1.isEmpty() || s1.peek().equals("(")) {//为空 或者 栈顶为“(” 直接放入
s1.push(item);
break;
} else if (getValue(item) > getValue(s1.peek())) {//如果优先级比栈顶运算符的高,也将运算符压入s1
s1.push(item);
break;
} else if (getValue(item) <= getValue(s1.peek())) {//优先级小于等于的时候将栈顶放入s2,然后在循环比较运算符
s2.add(s1.pop());
}
}
}
}
while (!s1.isEmpty()){
s2.add(s1.pop());
}
return s2;
}
//写一个方法,返回对应的优先级数字
public static int getValue(String operation) {
int result = 0;
switch (operation) {
case "+":
result = 1;
break;
case "-":
result = 1;
break;
case "*":
result = 2;
break;
case "/":
result = 2;
break;
default:
throw new RuntimeException("这不像是一个运算符哦!");
}
return result;
}
//计算的方法
public static double calculate(List<String> ls) {
//创建一个栈,只需要一个栈即可
Stack<String> stack = new Stack<String>();
//遍历ls
for (String item : ls) {
//匹配的是多位数
if (item.trim().matches("\\d+")) {
//入栈
stack.push(item.trim());
} else {
//pop出两个数,并运算,再入栈
double num1 = Double.parseDouble(stack.pop());
double num2 = Double.parseDouble(stack.pop());
double res = 0;
if (item.trim().equals("+")) {
res = num1 + num2;
} else if (item.trim().equals("-")) {
res = num2 - num1;
} else if (item.trim().equals("*")) {
res = num1 * num2;
} else if (item.trim().equals("/")) {
res = num2 / num1;
} else {
throw new RuntimeException("运算符有错误");
}
//把res 入栈
stack.push("" + res);
}
}
//最后留在stack中的数据是运算结果
return Double.parseDouble(stack.pop());
}
}