百度百科:
逆波兰表达式又叫做后缀表达式。逆波兰表示法是波兰逻辑学家J・卢卡西维兹(J・ Lukasewicz)于1929年首先提出的一种表达式的表示方法。后来,人们就把用这种表示法写出的表达式称作“逆波兰表达式”。逆波兰表达式把运算量写在前面,把算符写在后面。
博主理解:
逆波兰表达式,就是一种将平时人们所写的算术表达式(中缀表达式)按照一定的规则进行转换后形成的一种表达式形式
其好处:便于计算机的计算
咱们人所熟悉的算术表达式通常是这样的:先计算括号里的,然后再计算乘除,后加减,如果是同一级别的运算优先级,要从左向右依次计算,举个栗子:对于 1+(2+4)*5+8/2 这是一个咱们所常见的算术表达式,这种形式也叫中缀表达式,对于我们是如何计算的呢?
1 + (2+4)*5 + 8/2
1.= 1 + 6 * 5 + 8/2
2.= 1 + 30 + 8/2
3.= 1 + 30 + 4
4.= 31 + 4
5.= 35
上面的计算我不用过多解释,相信只要上过小学的都会。
下面我来说一下,如何把上面的中缀表达式通过一定的规则转换后得到的后缀表达式进行计算:
中缀表达式: 1+(2+4)*5+8/2 —>(规则)—> 后缀表达式: 124+5*+82/+
先不管规则是什么样的,先根据得到的后缀表达式,看看如何计算?
想要计算得到最终结果,这个过程需要用到栈结构(先进后出)
规则:遇到数字直接入栈,遇到操作符,则弹出两个项进行运算,将运算结果再入栈,最后栈底元素值则为最终结果
后缀表达式:124+5*+82/+
1.第一个元素为1,则1入栈,此时栈:1
2.第二个元素为2,则2入栈,此时栈:1 2
3.第三个元素为4,则4入栈,此时栈:1 2 4
4.第四个元素为+,则将栈内元素4和2弹出,并计算2+4=6,将结果6入栈,此时栈:1 6
5.第五个元素为5,则5入栈,此时栈:1 6 5
6.第六个元素为*,则将栈内元素5和6弹出,并计算6*5=30,将结果30入栈,此时栈:1 30
7.第七个元素为+,则将栈内元素30和1弹出,并计算1+30=31,将结果31入栈,此时栈:31
8.第八个元素为8,则8入栈,此时栈:31 8
9.第九个元素为2,则2入栈,此时栈:31 8 2
10.第十个元素为/,则将栈内元素2和8弹出,并计算8/2=4,将结果4入栈,此时栈:31 4
11.第十一个元素为+,则将栈内元素4和31弹出,并计算31+4=35,将结果35入栈,此时栈:35
12.遍历结束,将栈内结果35返回,所以结果为35
是不是感觉很神奇呢?
那么我们如何去将中缀表达式变为后缀表达式呢?
这就涉及到我们上面提到的规则!
规则:
1. 符号栈中,栈顶优先级最高
2. 向符号栈中加入符号时,要对比栈顶符号的优先级是否小于当前符号的优先级,如果小于则直接入栈,如果大于则要将栈内元素弹出,一直弹出至当前的栈顶符号的优先级小于当前符号,然后再将当前符号入栈
3. 如果入栈符号为 ( ,则直接入栈
4. 如果入栈符号为 ) ,则要循环弹栈,一直到栈顶元素为 ( ,为止,最后还要将栈顶元素 ( 弹出
优先级(这个在别人的博客里都没有终点强调过,但是我要重点强调一下):
+ – 优先级小于 * /,且 + 和 – 的优先级相同, * 和 / 的优先级相同
重点强调一下:(
它后面的符号元素优先级都比它要高,也就是后面可以随便加入符号元素
举个栗子:符号栈:(,此时要加入符号 + ,则直接可以加入,此时符号栈:( +
它可以放在任何优先级符号的后面
举个栗子:符号栈:+ *,此时要加入符号 (,则直接可以加入,此时符号栈:+ * (
( 0级或3级(0级是当 ( 后面可加入0级以上的优先级符号,3级是当碰到 ( 可直接加入到符号栈内) + – 1级 * / 2级
光说没有用,现在我们来尝试一下,将中缀表达式:1+(2+4)*5+8/2,变为后缀表达式:124+5*+82/+
中缀表达式:1+(2+4)*5+8/2
1. 第一个元素为1,则后缀表达式为:1,此时符号栈:空
2. 第二个元素为+,则后缀表达式为:1,此时符号栈:+
3. 第三个元素为(,则后缀表达式为:1,此时符号栈:+ (
4. 第四个元素为2,则后缀表达式为:12,此时符号栈:+ (
5. 第五个元素为+,则后缀表达式为:12,此时符号栈:+ ( +
6. 第六个元素为4,则后缀表达式为:124,此时符号栈:+ ( +
7. 第七个元素为),则后缀表达式为:124+,此时符号栈:+,(弹栈 + ( 并将 + 号连接到后缀表达式末尾)
8. 第八个元素为*,则后缀表达式为:124+,此时符号栈:+ *
9. 第九个元素为5,则后缀表达式为:124+5,此时符号栈:+ *
10. 第十个元素为+,则后缀表达式为:124+5*+,此时符号栈:+,(弹栈 * + 并将 * + 号连接到末尾,并将 + 号入栈)
11. 第十一个元素为8,则后缀表达式为:124+5*+8,此时符号栈:+
12. 第十二个元素为/,则后缀表达式为:124+5*+8,此时符号栈:+ /
13. 第十三个元素为2,则后缀表达式为:124+5*+82,此时符号栈:+ /
14. 遍历结束,然后将符号栈的所有符号弹栈连接到后缀表达式末尾,最后后缀表达式:124+5*+82/+
看!多么神奇就将上述的中缀表达式变为了后缀表达式,再根据我们上面说的后缀表达式求值的方式,就很容易得到一个咱们所正常书写的算术表达式的值了。
那么,我们程序怎么写呢?嘿嘿,我提供了 Java 版的中缀表达式变后缀表达式,后缀表达式求值的代码?:
class Operator {
Character sign;
Integer level;
Operator(Character sign, Integer level) {
this.sign = sign;
this.level = level;
}
public static boolean isOperator(String str) {
if (str == null || str.length() == 0) {
return false;
}
return "+".equals(str) || "-".equals(str) || "*".equals(str) || "/".equals(str);
}
}
public class Test {
public static void main(String[] args) {
System.out.println(Arrays.toString(infixExprStrToPostfixExprList("1+(2+4)*5+8/2").toArray()));
System.out.println(getPostfixExprVal(infixExprStrToPostfixExprList("1+(2+4)*5+8/2")));
}
/**
* 中缀表达式串 -> 后缀表达式集合
*
* @param infixExprStr 中缀表达式串
* @return 后缀表达式集合
*/
private static List<String> infixExprStrToPostfixExprList(String infixExprStr) {
char[] infixExprCharArr = infixExprStr.toCharArray();
Stack<Operator> signStack = new Stack<>();
StringBuffer numberBuffer = new StringBuffer();
List<String> postfixExprList = new ArrayList<>(20);
for (int index = 0; index < infixExprCharArr.length; index++) {
char ch = infixExprCharArr[index];
boolean isOperator = ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')';
if (!isOperator) {
numberBuffer.append(ch);
continue;
}
if (numberBuffer.length() != 0) {
postfixExprList.add(numberBuffer.toString());
numberBuffer.setLength(0);
}
switch (ch) {
case '+':
while (!signStack.isEmpty() && signStack.peek().level >= 1) {
postfixExprList.add(signStack.pop().sign.toString());
}
signStack.add(new Operator('+', 1));
break;
case '-':
while (!signStack.isEmpty() && signStack.peek().level >= 1) {
postfixExprList.add(signStack.pop().sign.toString());
}
signStack.add(new Operator('-', 1));
break;
case '*':
while (!signStack.isEmpty() && signStack.peek().level >= 2) {
postfixExprList.add(signStack.pop().sign.toString());
}
signStack.add(new Operator('*', 2));
break;
case '/':
while (!signStack.isEmpty() && signStack.peek().level >= 2) {
postfixExprList.add(signStack.pop().sign.toString());
}
signStack.add(new Operator('/', 2));
break;
case '(':
signStack.add(new Operator('(', 0));
break;
case ')':
while (!signStack.isEmpty() && signStack.peek().level != 0) {
postfixExprList.add(signStack.pop().sign.toString());
}
if (!signStack.isEmpty() && signStack.peek().level == 0) {
signStack.pop();
}
break;
default:
}
}
if (numberBuffer.length() != 0) {
postfixExprList.add(numberBuffer.toString());
}
while (!signStack.isEmpty()) {
postfixExprList.add(signStack.pop().sign.toString());
}
return postfixExprList;
}
/**
* 获取后缀表达式的值
* @param postfixExprList 后缀表达式集合
* @return 最终的计算值
*/
private static BigDecimal getPostfixExprVal(List<String> postfixExprList) {
Stack<String> calculateStack = new Stack<>();
for (String content : postfixExprList) {
if (Operator.isOperator(content)) {
String number2 = calculateStack.pop();
String number1 = calculateStack.pop();
String result = "";
switch (content) {
case "+":
result = strToNumber(number1).add(strToNumber(number2)).toString();
break;
case "-":
result = strToNumber(number1).subtract(strToNumber(number2)).toString();
break;
case "*":
result = strToNumber(number1).multiply(strToNumber(number2)).toString();
break;
case "/":
result = strToNumber(number1).divide(strToNumber(number2), 10, BigDecimal.ROUND_HALF_UP).toString();
break;
default:
}
calculateStack.push(result);
} else {
calculateStack.push(content);
}
}
return strToNumber(calculateStack.pop());
}
/**
* 数字串转数字
*
* @param numberStr 数字串
* @return BigDecimal 类型数字
*/
private static BigDecimal strToNumber(String numberStr) {
return new BigDecimal(numberStr);
}
}
上面的就是所有的实现代码了,请仔细阅读哦,如果有不清楚的可以在下方评论,对了,注意要用BigDecimal数据类型,这个类型作除法等等涉及到浮点运算比较精确,嘿嘿,如果有想法的读者可以根据上述的逆波兰表达式,做一个表达式计算器,配合上swing 可以做一个应用程序了,嘻嘻(●’◡’●)!