- 一、前缀表达式
- (1)中缀表达式转前缀表达式
- (2)前缀表达式的计算
- 二、后缀表达式
- (1)中缀表达式转后缀表达式
- (2)后缀表达式的计算
- 三、中缀表达式直接计算
- 四、总结
代码实现的工具类ExpressionUtils,类中有两个静态方法,calculateResult方法是实现两数的加减乘除操作,getPriority方法获取加减乘除运算符的优先级,代码如下:
/**
* @Package:前缀表达式和中缀表达式和后缀表达式大全
* @ClassName:GetCalculate
* @Author:ZQX
* @Date:2021/7/9 22:17
* @Description:把两个整型数据进行加减乘除操作,第一个形参数据对第二形参数据进行加减乘除操作
*/
public class ExpressionUtils {
//两个整型数据进行加减乘除操作,第一个形参数据对第二形参数据进行加减乘除操作
public static String calculateResult(int num1,int num2,String str){
int result = 0;
switch (str){
case "*":
result =num1*num2;
break;
case "/":
result =num1/num2;
break;
case "+":
result =num1+num2;
break;
case "-":
result =num1-num2;
break;
default:
break;
}
return ""+result;
}
//获取加减乘除运算符的优先级
public static int getPriority(String element) {
if (element.matches("[*/]")) {
return 2;
}
if (element.matches(("[+-]"))) {
return 1;
}
return 0;
}
}
一、前缀表达式
(1)中缀表达式转前缀表达式
正常数字计算表达式是属于中缀表达式的,比如:16*4+(4/2)-25,中缀表达式转换为前缀表达式的思路如下:
有两个栈,s1栈用来存储运算符或括号,s2栈存储操作数
1.遍历表达式,从右到左扫描,一个个判断
2.判断字符是数字就直接压栈入s2
3.判断字符是右括号的话,直接压栈入s1
4.判断字符是左括号的话:
需要判断s1栈顶的元素是否为右括号,不是右括号弹栈出来压入到s2栈,继续判断栈顶的元素是否为右括号,直到栈顶的元素是右括号才结束,这时也要把左括号弹栈出来,不能把左扩号留在s1栈中
5.判断字符是运算符的情况下:
5.1如果s1栈顶元素为右括号或者栈为空,可以直接把字符压入到s1栈中
5.2如果s2栈顶元素也是运算符,判断该字符的优先级和栈顶元素的优先级大小,如果字符的优先级大于等于栈顶元素的优先级,直接把字符压入到s1栈中,------如果优先级小于的话,需要把栈顶元素弹栈压入s2栈中,在继续判断栈顶元素是什么,也就是回到了步骤5
6.扫描到表达式左边,结束遍历,把s1里的栈元素依次弹栈压入到s2栈中
7.s2栈中依次顺序弹栈的出来的排序就是前缀表达式
(2)前缀表达式的计算
1.定义一个栈 s 存储操作数
2.从右到左扫描前缀表达式
3.扫描的是数字,直接压入s栈
4.扫描的是运算符,弹出栈顶的第一个元素,在弹出栈顶的第二个元素,运算为第一个元素 运算符 第二个元素 这个顺序 计算得到结果,把结果压入s栈
5.扫描到表达式最左边,结束扫描,这时s栈中只有一个元素,其元素就是为前缀表达式的计算结果
中缀表达式转换为前缀表达式以及前缀表达式计算结果的代码实现如下:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Stack;
/**
* @Package:前缀表达式和中缀表达式和后缀表达式大全
* @ClassName:PrefixExpression
* @Author:ZQX
* @Date:2021/7/9 20:37
* @Description:前缀表达式计算值
*/
public class PrefixExpression {
public static void main(String[] args) {
//正常的中缀表达式
//String str = "16*4+(4/2)-25";
//把每个数值存到list集合里
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "16", "*", "4", "+", "(", "4", "/", "2", ")", "-", "25");
System.out.println("中缀表达式为:" + list);
ArrayList<String> prefixExpression = infixConvertPrefixExpression(list);
System.out.println("中缀表达式转成前缀表达式的为:" + prefixExpression);
int result = prefixExpressionCaculate(prefixExpression);
System.out.println("前缀表达式的计算结果为:" + result);
}
/**
* 把中缀表达式转换为前缀表达式,返回一个前缀表达式的ArrayList集合存储
* @param list
* @return
*/
public static ArrayList<String> infixConvertPrefixExpression(ArrayList<String> list) {
// 创建栈S1存储运算符和括号,ArrayList集合对象为S2
Stack<String> s1 = new Stack<>();
ArrayList<String> s2 = new ArrayList<>();
//把前缀表达式的字符顺序存储到集合s3
ArrayList<String> s3 = new ArrayList<>();
//从右往左扫描中缀式,也就是从后往前遍历list集合
for (int i = list.size() - 1; i >= 0; i--) {
//获取集合索引值元素给一个String变量element
String element = list.get(i);
//操作数:将元素添加到s2集合
if (element.matches("\\d+")) {
s2.add(element); //把元素添加到s2集合
} else if (element.matches("[)]")) { //若是右括号),将其压入S1栈
s1.push(element);
} else if (element.matches("[(]")) { //若是左括号‘(’,则依次弹出S1栈的元素,并将其压入S2栈,直至S1栈栈顶元素为右括号‘)’,弹出这个右括号
//循环弹出s1栈的栈顶元素,直到栈顶元素为左括号为止
while (!s1.peek().matches("[)]")) {
//把栈顶元素弹出添加到s2集合里
s2.add(s1.pop());
}
//这时候s1栈顶是右括号,把右括号弹出
s1.pop();
} else { //遇到运算符时
//进入死循环
while (true) {
if (s1.size() == 0 || s1.peek().matches("[)]")) {
//若S1栈为空或者其栈顶元素为右括号‘)’,将元素压入S1栈
s1.push(element);
//跳出死循环
break;
} else if (ExpressionUtils.getPriority(element) >= ExpressionUtils.getPriority(s1.peek())) {
//比较运算符和S1栈顶运算符(一定是运算符)的优先级
// 若元素的优先级≥S1栈顶运算符优先级则将元素压入S1栈
s1.push(element);
//跳出死循环
break;
} else { //执行到这里说明元素的优先级小于<S1栈顶运算符优先级
// 若小于,则将S1栈顶运算符弹出并添加到S2集合,继续死循环
s2.add(s1.pop());
}
}
}
}
//执行到这里遍历完了表达式
//将S1剩余元素压入S2。最后依次取出S2元素即为前缀表达式。
while (s1.size() != 0) {
s2.add(s1.pop());
}
//此时s2集合的元素为25,2,4,/,4,16,*,+,-
// 前缀表达式为-, +, *, 16, 4, /, 4, 2, 25,需要逆向获取s2集合的元素
//循环遍历,把元素存入到s3集合
for (int j = s2.size() - 1; j >= 0; j--) {
s3.add(s2.get(j));
}
//返回s3集合
return s3;
}
public static int prefixExpressionCaculate(ArrayList<String> list) {
//创建一个栈,存储数字
Stack<String> operStack = new Stack<>();
//从集合的最后一个索引值开始遍历,从右向左扫描
for (int i = list.size() - 1; i >= 0; i--) {
//获取集合索引值元素给一个String变量number
String number = list.get(i);
//如果number是数字直接存入operStack栈中
if (number.matches("\\d+")) {
operStack.push(number);
} else {
//不是数字就把operStack栈顶的两个元素取出来
int num1 = Integer.valueOf(operStack.pop());
int num2 = Integer.valueOf(operStack.pop());
//调用工具类ExpressionUtils的静态方法calculateResult进行计算,把结果赋值给result
String result = ExpressionUtils.calculateResult(num1, num2, number);
//把计算后的结果压入operStack栈中
operStack.push(result);
}
}
//遍历完集合后,此时operStack栈中只有一个元素,把元素取出打印结果
int calucateResult = Integer.valueOf(operStack.pop());
return calucateResult;
}
代码运行结果截图如下:
二、后缀表达式
(1)中缀表达式转后缀表达式
中缀表达式转后缀表达式跟中缀表达式只是有些许区别,其他思路基本一样,其区别为:
1.扫描中缀表达式,是从左到右扫描的
2.扫描到左括号入栈,右括号才需要判断栈顶元素
3.扫描到运算符优先级大于栈顶元素运算符就入栈,而前缀表达式的是优先级大于等于栈顶元素运算符才入栈,否则需要弹栈顶元素
4.后缀表达式计算值时是弹栈顶的第二个元素 运算符 弹栈顶的第一个元素 顺序进行计算结果,跟前缀是相反的
中缀表达式转后缀表达式思路:
定义一个栈s1,存储运算符或者左括号;一个栈s2,存储操作数
1.从左到右扫描中缀表达式
2.扫描的是操作数,直接压入栈s2
3.扫描的是左括号,直接压入栈s1
4.扫描的是右括号,判断s1栈顶元素不是左括号,就弹栈把栈顶元素压入s2,继续扫描栈顶元素,直到栈顶元素是左括号为止,这时需要把左括号弹出来,弹完扔掉即可-。-
5.扫描的是运算符的情况:
5.1 判断s1栈顶元素为左扩号或者s1栈为空时,就把运算符压入s1栈中
5.2 不是,那栈顶元素就是运算符,如果扫描的运算符优先级大于s1栈顶运算符的优先级,那就把扫描的运算符压入s1栈中(比如 扫描的运算符为乘号" * “,栈顶元素为减号 " -” ,那我乘号就直接入s1栈),如果优先级小于等于的话,就需要把s1栈顶运算符弹出压入到s2栈中,然后继续判断栈顶元素,就是回到了步骤5
6.扫描中缀表达式到最右边,把s1栈里的元素(运算符)弹出压入s2栈中
7.把s2栈的元素依次弹出,逆序排序后才为后缀表达式
(2)后缀表达式的计算
1.定义一个栈 s 存储操作数
2.从左到右扫描后缀表达式
3.扫描的是数字,直接压入s栈
4.扫描的是运算符,弹出栈顶的第一个元素,在弹出栈顶的第二个元素,运算为第二个元素 运算符 第一个元素 这个顺序 计算得到结果,把结果压入s栈
5.扫描到表达式最右边,结束扫描,这时s栈中只有一个元素,其元素就是为后缀表达式的计算结果
中缀表达式转换为后缀表达式以及前缀表达式计算结果的代码实现如下:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Stack;
/**
* @Package:前缀表达式和中缀表达式和后缀表达式大全
* @ClassName:SuffixExpression
* @Author:ZQX
* @Date:2021/7/10 9:52
* @Description:后缀表达式
*/
public class SuffixExpression {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "16", "*", "4", "+", "(", "4", "/", "2", ")", "-", "25");
System.out.println("中缀表达式为:" + list);
ArrayList<String> suffixExpression = infixConvertSuffixExpression(list);
System.out.println("中缀表达式转为后缀表达式后为:" + suffixExpression);
System.out.println("后缀表达式的计算结果为:"+suffixExpressionCaculate(suffixExpression));
}
/**
* 后缀表达式的计算
* @param list 后缀表达式的ArrayList集合
* @return 返回一个int的后缀表达式结果
*/
public static int suffixExpressionCaculate(ArrayList<String> list) {
//创建一个栈,存储数字
Stack<String> operStack = new Stack<>();
//从后缀表达式左扫描到右
for (int i = 0; i < list.size(); i++) {
String number = list.get(i);
//如果是数字直接存入栈中
if (number.matches("\\d+")) {
operStack.push(number);
} else {
//不是数字就把栈顶的两个元素取出来
int num1 = Integer.valueOf(operStack.pop());
int num2 = Integer.valueOf(operStack.pop());
//进行计算
String result = ExpressionUtils.calculateResult(num2, num1, number);
//把计算后的结果压入操作数栈中
operStack.push(result);
}
}
//遍历完集合后,栈中只有一个元素,把元素取出打印结果
int calucateResult = Integer.valueOf(operStack.pop());
return calucateResult;
}
/**
* 把中缀表达式转成后缀表达式
* @param list 中缀表达式的集合
* @return 返回后缀表达式的集合
*/
public static ArrayList<String> infixConvertSuffixExpression(ArrayList<String> list) {
//中缀转后缀
//创建一个栈,存运算符栈 ,s1
Stack<String> s1 = new Stack<>();
//创建一个list集合,存操作数,相当于栈 s2,因为用栈的话弹出还要逆序遍历,所以直接放在集合里
ArrayList<String> s2 = new ArrayList<>();
//从中缀表达式从左到右扫描,就是正序遍历list集合
for (int i = 0; i < list.size(); i++) {
//获取集合索引值元素,存储到String遍历element
String element = list.get(i);
//判断该元素,是数字,添加到s2集合里
if (element.matches("\\d+")) {
s2.add(element);
} else if (element.matches("[(]")) { //是左括号,直接入s1栈
s1.push(element);
} else if (element.matches("[)]")) {
//如果是右括号,取s1栈顶元素到list集合,直到该栈顶元素为左括号为止
while (!s1.peek().matches("[(]")){
//把s1栈顶元素弹出添加到s2集合
s2.add(s1.pop());
}
//执行到这里,栈顶元素为左括号,把该左括号弹出栈
s1.pop();
} else { //执行到这里说明是运算符号
//死循环
while (true) {
//如果栈顶元素是左括号或者栈为空就直接入栈
if (s1.size() == 0 || s1.peek().matches("[(]")) {
s1.push(element);
//结束循环
break;
} //不是就要判断当前元素跟栈顶元素的优先级大小
else if (ExpressionUtils.getPriority(element) > ExpressionUtils.getPriority(s1.peek())) {
//如果当前元素的优先级大于栈顶元素的优先级,直接进栈
s1.push(element);
//结束循环
break;
}else{ //当前元素的优先级小于等于栈顶元素的优先级
//把栈顶元素弹出 添加到集合s2
s2.add(s1.pop());
//继续判断栈顶元素是左括号还是栈为空还是运算符比较优先级
//加循环
}
}
}
}
//集合遍历结束,把s1栈的元素弹出添加到集合中
while(s1.size()!=0){
s2.add(s1.pop());
}
//后缀表达式s2如果用栈存储,弹出的时候还要逆序回来,用集合就不用
return s2;
}
}
代码执行结果截图如下所示:
三、中缀表达式直接计算
中缀表达式是属于我们日常生活用的计算表达式,转为前缀后缀表达式计算机才好去计算,中缀表达式直接计算对计算机来说比较难以理解,中缀表达式计算结果的思路:
定义两个栈,s1栈存储运算符和左括号,s2栈存储操作数
1.从左到右扫描中缀表达式
2.扫描的是数字直接压入s2栈
3.扫描的是左括号,直接压入s1栈
4.扫描的是右括号,判断s1栈顶元素是不是左括号,不是左括号(那就是运算符了)就把栈顶元素弹栈,s2栈的就弹栈出两个元素,第二个元素 运算符 第一元素 这样的顺序计算结果,把结果压入s2栈中,继续判断栈顶元素是不是左括号,继续这样的步骤,直到栈顶元素为左括号为止
5.扫描的是运算符:
5.1判断s1栈顶元素是左括号或者s1栈为空的话,直接压入到s1栈
5.2如果栈顶元素是运算符,扫描的运算符优先级大于栈顶运算符的优先级,把扫描的运算符压入s1栈,如果是小于等于的话,把栈顶的运算符弹出,s2栈弹出两个元素,第二个元素 运算符 第一元素 这样的顺序计算结果,把结果压入s2栈中,把扫描到的运算符压入栈s1中
6.扫描到中缀表达式的最右边,s1栈弹出一个运算符,s2栈弹出两个操作数,第二个元素 运算符 第一元素 这样的顺序计算结果,把结果压入s2栈中,依次顺序执行下去,直到s1栈空了为止 (其实遍历完中缀表达式后,s1栈最多剩两个运算符)
7.这时s2栈中只有一个操作数,那就是中缀表达式的计算结果
中缀表达式计算结果的代码实现如下:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Stack;
/**
* @Package:前缀表达式和中缀表达式和后缀表达式大全
* @ClassName:InfixExpression
* @Author:ZQX
* @Date:2021/7/9 21:56
* @Description:中缀表达式
*/
public class InfixExpression {
public static void main(String[] args) {
//85*4+9+7-25
//85*4+9/7-25
//给计算机算的话会出错,计算机是从左往右开始一个个计算结果会把85*4后+9再/7再-25=24
//这种情况需要加括号转成后缀表达式
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "16", "*", "4", "+", "(", "4", "/", "2", ")", "-", "25");
int result = infixExpressionCaculate(list);
System.out.println("中缀表达式的计算结果为:" + result);
}
public static int infixExpressionCaculate(ArrayList<String> list) {
//创建一个运算符s1栈,存储运算符
Stack<String> s1 = new Stack<>();
//创建一个操作符s2栈,存储数字
Stack<String> s2 = new Stack<>();
//遍历中缀表达式集合,从左往右扫描
for (int i = 0; i < list.size(); i++) {
//获取集合索引值元素给一个String变量element
String element = list.get(i);
//判断该元素是数字的话
if (element.matches("\\d+")) {
//如果是数字直接压入s2栈
s2.push(element);
} else if (element.matches("[(]")) { //如果该元素是左括号的话
//把元素压入到s1栈
s1.push(element);
} else if ((element.matches("[)]"))) { //如果该元素是右括号的话
while (!s1.peek().matches("[(]")) { //循环,直到栈顶元素为左括号为止
int num1 = Integer.valueOf(s2.pop()); //弹出s2栈的第一个元素转成int类型
int num2 = Integer.valueOf(s2.pop()); //弹出s2栈的第二个元素转成int类型
String sign = s1.pop(); //弹出s1栈顶的运算符
String result = ExpressionUtils.calculateResult(num2, num1, sign); //调用方法计算结果
s2.push(result); //计算结果压入s2栈中
}
//执行到这里,说明栈顶元素是左括号,把左括号弹出扔掉咯
s1.pop();
} else { //执行到这里,说明该元素是运算符,进行判断
//如果是运算符栈为空或者栈顶元素为左括号或者该元素的优先级大于栈顶元素的优先级 就直接入栈
if (s1.size() == 0 || s1.peek().matches("[(]") || ExpressionUtils.getPriority(element) > ExpressionUtils.getPriority(s1.peek())) {
s1.push(element);
} else {
//如果该元素的优先级小于等于栈顶元素的优先级,那么就弹出操作符栈顶的二个数据,
int num1 = Integer.valueOf(s2.pop()); //弹出s2栈的第一个元素转成int类型
int num2 = Integer.valueOf(s2.pop()); //弹出s2栈的第二个元素转成int类型
String sign = s1.pop(); //弹出s1栈顶的运算符
String result = ExpressionUtils.calculateResult(num2, num1, sign); //调用方法计算结果
//计算结果压入s2栈中
s2.push(result);
// 再把该元素运算符压入s1栈中
s1.push(element);
}
}
}
//遍历完集合,依次从s2栈中弹出栈顶两个操作数
//s1栈中弹出一个运算符进行计算再入s2栈,直到s1栈为空
while (s1.size() != 0) {
int num1 = Integer.valueOf(s2.pop());
int num2 = Integer.valueOf(s2.pop());
String sign = s1.pop();
String result = ExpressionUtils.calculateResult(num2, num1, sign);
s2.push(result);
}
//循环结束,这时s2栈只有一个操作数,就是计算结果,把结果打印出来
int result = Integer.valueOf(s2.pop());
return result;
}
}
四、总结
知道思路,熟练到能手打字出来就能掌握这东西了,有点绕的就是优先级的比较,优先级的比较总结截图如下所示:
还有就是如何把一个表达式字符串分割成数字、运算符、左括号、右括号、存到集合里,下篇文章就想个思路和代码实现这个东西!