文章目录
前言
生活中最常见的就是中缀表达式了吧,比如(3 + 4) * 5 - 6这种形式,但是对于计算机来说,前缀和后缀表达式更适合运算。详细介绍如下:
一、前缀、中缀、后缀表达式介绍
1. 前缀表达式:
前缀表达式的运算符位于操作数之前,又称波兰式;
前缀表达式计算机求值:
从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算
并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果;
2. 中缀表达式:
常见的运算表达式
中缀表达式的求值:
中缀表达式是我们人最熟悉的,但是对计算机来说却不好操作(上篇文章中的求解);
因此,在计算结果时,往往会将中缀表达式转成其它表达式来操作(一般转成后缀表达式.)
3. 后缀表达式:
后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后
后缀表达式的计算机求值
从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算
并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果
正常的表达式(中缀) | 逆波兰表达式 (后缀) |
---|---|
a+b | a b + |
a+(b-c) | a b c - + |
a+(b-c)*d | a b c - d * + |
a+d*(b-c) | a d b c - * + |
a=1+3 | a 1 3 + = |
二、后缀表达式求解
1.实现目标
1. 输入一个逆波兰表达式(后缀表达式),使用栈(Stack), 计算其结果
2. 支持小括号和多位数整数的计算
2.思路分析
1. 先将表达式转换成列表类型(ArrayList)
2.1 将数据和运算符放入ArrayList
代码如下:
public static List<String> getListString(String string) {
//将字符串分割
String[] s = string.split(" ");
List<String> list = new ArrayList<String>();
for (String a : s) {
list.add(a);
}
return list;
}
2.2 完成后缀表达式的计算
这里用正则表的是匹配多位数字(方便)
//完成对逆波兰表达式的计算
public static int calculate(List<String> list) {
//创建栈,只需要一个栈即可(数字)
Stack<String> numStack = new Stack<String>();
for (String s : list) {
//正则表达式来取数字方便一些
if (s.matches("\\d+")) {//匹配的是多位数
numStack.push(s);
} else {
int num1 = Integer.parseInt(numStack.pop());
int num2 = Integer.parseInt(numStack.pop());
int ans = 0;
if (s.equals("+")) {
ans = num2 + num1;
} else if (s.equals("-")) {
ans = num2 - num1;
} else if (s.equals("*")) {
ans = num2 * num1;
} else if (s.equals("/")) {
ans = num2 / num1;
}else{
throw new RuntimeException("运算符错误");
}
numStack.push("" + ans);
}
}
return Integer.parseInt(numStack.pop());
}
三、中缀表达式转后缀表达式
用户一般输入的都是中缀表达式,如果能自动转为后缀表达式即可调用方法计算:
1. 思路分析
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中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
2. 代码实现:
2.1 将中缀表达式转为对应的List
为了运算方便,我们将表达式转为对应的ArrayList
代码如下:
//将中缀表达式转成对应的List
public static List<String> toInfixExperssionList(String expression){
List<String> list = new ArrayList<>();
int i = 0;
String s = "";
char c; //遍历的
for(i = 0 ; i < expression.length() ; ){
//如果c不是数字:----加入到list中即可:
c = expression.charAt(i);
if(c > '9' || c < '0'){
list.add("" + c);
i++;
}else{//如果是数字的话, 先清空上次的数据,再拼接
s = "";
while(i < expression.length() && (c=expression.charAt(i)) >= '0'
&&(c=expression.charAt(i)) <= '9'){
s += c;
i++;
}
list.add(s);
}
}
return list;
}
2.2 中缀转后缀的代码实现:
//将中缀表达式转后缀表达式的代码:
public static List<String> parseSuffixExpresionList(List<String> mid){
Stack<String> oper = new Stack<String>();
// Stack<String> temp = new Stack<String>();//其实没有必要用,用一个StringBuilder更好
//因为temp在转换的时候, 没有出栈的操作, 所以我们直接用ArrayList<>即可;
List<String> after = new ArrayList<String>();
for(String list : mid){
//如果是一个数字,就进after:
if(list.matches("\\d+")){
after.add(list);
}else if(list.equals("(")){
oper.push(list);
}else if(list.equals(")")){//右括号的话弹栈
while(!oper.peek().equals("(")){
after.add(oper.pop());
}
//两个括号抵消
oper.pop();
}else{
//若读取的list操作符优先级 小于等于 oper栈顶部的优先级,,oper出栈到 after
while(!oper.isEmpty() &&
Operation.getValue(list) <= Operation.getValue(oper.peek())){
after.add(oper.pop());
}
//直到list操作符优先级 大于oper栈顶优先级:
oper.push(list);
}
}
while (!oper.isEmpty()) {
after.add(oper.pop());
}
return after;
}
总结
只要按照步骤及进行即可,主要还是对于多位数的处理,以及对于括号的处理;完整代码:
package 栈;
import javax.imageio.event.IIOReadProgressListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.SubmissionPublisher;
import java.util.stream.StreamSupport;
/**
* @author Weilei Zhang
* @create 2021-03-01 19:47
*/
public class PolanNotation {
public static void main(String[] args) {
//将中缀表达式转后缀表达式的代码:
//但是因为扫描字符串不方便,所以在此将中缀表达式转成对应的List
String expresion = "1+((2+3)*4)-5";
//进行转换为List<String>
List infixList = toInfixExperssionList(expresion);
System.out.println("前缀表达式List = " + infixList);
List<String> suffix = parseSuffixExpresionList(infixList);
System.out.println("后缀表达式List: " + suffix);
System.out.println(expresion + " = " + calculate(suffix));
// //先写一个逆波兰表达式(后缀表达式)
// String str = "3 4 + 5 * 6 -";//(3 + 4) * 5 - 6 = 29
// String str2 = "12 3 + 4 5 * +";//(12 + 3) + 4 * 5 = 35
// //一个表达式 依次将数据和运算符放到ArrayList中, 配合栈,完成计算
// List<String> list = getListString(str2);
// System.out.println("list = " + list);
// System.out.println("answer = " + calculate(list));
}
public static List<String> getListString(String string) {
//将字符串分割
String[] s = string.split(" ");
List<String> list = new ArrayList<String>();
for (String a : s) {
list.add(a);
}
return list;
}
// 完成对逆波兰表达式的计算
public static int calculate(List<String> list) {
//创建栈,只需要一个栈即可(数字)
Stack<String> numStack = new Stack<String>();
for (String s : list) {
//正则表达式来取数字方便一些
if (s.matches("\\d+")) {//匹配的是多位数
numStack.push(s);
} else {
int num1 = Integer.parseInt(numStack.pop());
int num2 = Integer.parseInt(numStack.pop());
int ans = 0;
if (s.equals("+")) {
ans = num2 + num1;
} else if (s.equals("-")) {
ans = num2 - num1;
} else if (s.equals("*")) {
ans = num2 * num1;
} else if (s.equals("/")) {
ans = num2 / num1;
}else{
throw new RuntimeException("运算符错误");
}
numStack.push("" + ans);
}
}
return Integer.parseInt(numStack.pop());
}
//将中缀表达式转后缀表达式的代码:
public static List<String> parseSuffixExpresionList(List<String> mid){
Stack<String> oper = new Stack<String>();
// Stack<String> temp = new Stack<String>();//其实没有必要用,用一个StringBuilder更好
//因为temp在转换的时候, 没有出栈的操作, 所以我们直接用ArrayList<>即可;
List<String> after = new ArrayList<String>();
for(String list : mid){
//如果是一个数字,就进after:
if(list.matches("\\d+")){
after.add(list);
}else if(list.equals("(")){
oper.push(list);
}else if(list.equals(")")){//右括号的话弹栈
while(!oper.peek().equals("(")){
after.add(oper.pop());
}
//两个括号抵消
oper.pop();
}else{
//若读取的list操作符优先级 小于等于 oper栈顶部的优先级,,oper出栈到 after
while(!oper.isEmpty() &&
Operation.getValue(list) <= Operation.getValue(oper.peek())){
after.add(oper.pop());
}
//直到list操作符优先级 大于oper栈顶优先级:
oper.push(list);
}
}
while (!oper.isEmpty()) {
after.add(oper.pop());
}
return after;
}
//将中缀表达式转成对应的List
public static List<String> toInfixExperssionList(String expression){
List<String> list = new ArrayList<>();
int i = 0;
String s = "";
char c; //遍历的
for(i = 0 ; i < expression.length() ; ){
//如果c不是数字:----加入到list中即可:
c = expression.charAt(i);
if(c > '9' || c < '0'){
list.add("" + c);
i++;
}else{//如果是数字的话, 先清空上次的数据,再拼接
s = "";
while(i < expression.length() && (c=expression.charAt(i)) >= '0'
&&(c=expression.charAt(i)) <= '9'){
s += c;
i++;
}
list.add(s);
}
}
return list;
}
}
class Operation{
private static int ADD = 1;
private static int SUB = 1;
private static int MUL = 2;
private static int DIV = 3;
//获得优先级
public static int getValue(String s){
int result = 0;
switch (s){
case "+":
result = ADD;
break;
case "-":
result = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
result = -1;
break;
}
return result;
}
}