该篇中表达式运算(加减乘除 取余 幂运算),包括小数,带有各种英文括号,以及多余符号的去除,有关的原理也可以参照上一篇文章.
一.后缀表达式
(1) 简介:
又称逆波兰式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则),使用中缀表达式符合人的思维习惯,但是后缀表达式便于计算机.
因此,我们将中缀表达式转换为后缀表达式在作计算.
(2) 后缀表达式求值
思路:
设置一个栈.遍历表达式.
- 如果遇到操作数,压入栈中
- 如果遇到运算符,从栈顶弹出两个操作符并且做运算,运算结果压入栈中.
- 栈中最后留下的数就是最终的结果.
举例: 4 5 * 8 - 60 + 8 2 / +
- 操作数压入栈中,因此,4 和 5压入栈中
- 遇到运算符*号,从栈中弹出两个操作数,进行运算,结果压入栈中,弹出4和5,结果20压栈.
- 遇到8压栈
- 遇到 - 号,计算,得到结果 12 压入栈中
- 遇到60 压入栈中
- 遇到 + 号,计算得到结果72
- 8 和 2压入栈中
- / ,弹出8 和 2,计算得到4,压入栈中
- +, 弹出72 和 4 ,计算,得到76
- 最终结果为76
(3) 后缀表达式求值代码实现
private static float calculate(List<String> s) {
Stack<String> stack = new Stack<>();
for (String it : s) {
// 如果为操作数,直接入栈
if (it.matches("[1-9]\\d*\\.?\\d*")) {
stack.push(it);
} else {
// 如果为运算符,则从栈顶弹出两个操作数,并进行运算,将结果压入栈中
float num1 = Float.parseFloat(stack.pop());
float num2 = Float.parseFloat(stack.pop());
float res = 0;
switch (it) {
case "+": {
res = num1 + num2;
break;
}
case "-": {
res = num2 - num1;
break;
}
case "*": {
res = num2 * num1;
break;
}
case "/": {
res = num2 / num1;
break;
}
case "%": {
res = num2 % num1;
break;
}
case "^": {
res = 1;
for (int i = 1; i <= num1; i++) {
res *= num2;
}
break;
}
default: {
System.out.println("该符号不正确");
res = -999999;
break;
}
}
stack.push(res + "");
}
}
return Float.parseFloat(stack.pop());
}
二.中缀表达式转后缀表达式
(1) 思路:
-
初始化一个栈operS,该栈用来存储符号;初始化一个集合res,用来存储中间结果.
-
从左至右扫描中缀表达式
-
如果遇到操作数时,直接加入到res中.
-
如果遇到括号时
A. 为左括号,直接压入栈operS中
B. 为右括号,依次弹出operS栈顶的运算符加入到res中,知道遇到左括号,此时,将这一对括号舍弃. -
如果遇到运算符,比较其与operS栈顶运算符的优先级.
A. 如果栈空,或者栈顶为左括号,或者当前运算符优先级 > 栈顶运算符优先级,则将该运算符压入栈中.
B. 否则,如果当前运算符优先级 <= 栈顶运算符优先级,则弹出栈顶运算符加入到res中,再次与新的栈顶运算符优先级作比较. -
重复以上步骤,直到表达式扫描结束.
-
将operS栈中剩余运算符依次弹出并加入到res集合中
-
res中保存着的就是转换后的后缀表达式.
(2) 举例: 4*5-(8+6)*2+8/2




最后的结果就是45 * 8 6 + 2 * - 8 2 /
(3) 代码实现
调用
public static float houCaculate(String str){
// 将中缀表达式转换为集合
List<String> list = toHouList(str);
// 将中缀表达式集合转换为后缀表达式
List<String> list1 = toHou(list);
// 计算后缀表达式
return calculate(list1);
}
将中缀表达式转换为后缀表达式
/**
* 将中缀表达式转换为后缀表达式
*
* @param list
* @return
*/
private static List<String> toHou(List<String> list) {
// 设置一个符号栈
Stack<String> operS = new Stack<>();
// 设置List集合装结果
List<String> res = new ArrayList<>();
for (String it : list) {
// 如果当前指向为操作数,直接入集合
if (it.matches("[1-9]\\d*\\.?\\d*")) {
res.add(it);
} else if (isLeft(it) || operS.isEmpty() || isLeft(operS.peek())) {
// 如果当前指向为左括号,或者栈是空的,或者栈顶是左括号,直接入符号栈
operS.push(it);
} else if (isRight(it)) {
// 符号栈一直出栈,直到遇到左括号.出栈的符号加入到集合中
while (!isLeft(operS.peek())) {
res.add(operS.pop());
}
// 将右括号舍弃
if (priority(it) == priority(operS.peek()))
operS.pop();
else {
System.out.println("括号匹配有问题");
return null;
}
} else if (priority(operS.peek()) < priority(it)) {
// 当前运算符优先级大于栈顶优先级
operS.push(it);
} else {
while (!operS.isEmpty() && priority(operS.peek()) >= priority(it)) {
res.add(operS.pop());
}
operS.push(it);
}
}
while (!operS.isEmpty()) {
res.add(operS.pop());
}
return res;
}
// 判断是否是一个数字
private static boolean isNumber(char c) {
if (c >= '0' && c <= '9')
return true;
return false;
}
// 过滤一些特殊符号
private static String filtration(String str){
String regEx = "[`~!@#$&|:<>?~!@#¥%……&()——|【】‘;:”“’。,、?\t\n]";
return Pattern.compile(regEx).matcher(str).replaceAll("").replace(" ","");
}
将中缀表达式转换成List
/**
* 将中缀表达式转换成List
*
* @param str
* @return
*/
private static List<String> toHouList(String str) {
// 设置一个index索引遍历s
int index = 0;
String s = filtration(str);
// 设置List装结果
List<String> list = new ArrayList<>();
do {
// 如果当前指向是符号
if (!isNumber(s.charAt(index))) {
list.add(s.charAt(index) + "");
index++;
}
// 如果当前指向为数字
else {
String num = "";
while (isNumber(s.charAt(index)) || s.charAt(index) == '.') {
num += s.charAt(index);
index++;
if(index == s.length()) break;
}
// 避免num中出现多个小数点,或者第一位为小数点
if (num != "" && num.charAt(0) != '.' && num.split("\\.").length <= 2)
list.add(num);
}
} while (index <= s.length() - 1);
return list;
}
返回运算符的优先级
/**
* 返回运算符的优先级
*
* @param oper
* @return
**/
// 返回元素的优先级
private static int priority(String oper) {
int val = 100;
switch (oper) {
case "*":
case "%":
case "/": {
val = 1;
break;
}
case "^": {
val = 2;
break;
}
case "+":
case "-": {
val = 0;
break;
}
case "[":
case "]": {
val = -1;
break;
}
case "{":
case "}": {
val = -2;
break;
}
case "(":
case ")": {
val = -3;
break;
}
}
return val;
}
判断左括号或者右括号
// 判断是否为左括号
private static boolean isLeft(String v) {
if (v.equals("(") || v.equals("[") || v.equals("{")) return true;
return false;
}
// 判断是否为右括号
private static boolean isRight(String v) {
if (v.equals(")") || v.equals("]") || v.equals("}")) return true;
return false;
}
这篇博客介绍了如何在Java中处理后缀表达式求值和中缀表达式转后缀表达式的过程。通过设置栈进行计算,详细解释了每个步骤,并提供了具体的操作数和运算符的处理示例。最后提到了中缀表达式转换的代码实现思路。
--java版本&spm=1001.2101.3001.5002&articleId=98616869&d=1&t=3&u=e3a9b6aeb3a545c3951eb4b49c90f540)
6186

被折叠的 条评论
为什么被折叠?



