中缀表达式
中缀表达式就是通常我们在数学中看到的表达式,是一种人类易读、易理解的表达式,更能符合人类通常习惯性思维的表达式
如:( 3 * 4 ) + 20 - 50 / 2
缺点:对于计算机很难操作。
前缀表达式
顾名思义,符号在前,数字在后,成为前缀
如:- * + 3 4 5 6,其对应的中缀表达式为:( 3 + 4 ) * 5 - 6
前缀表达式求值思路:
从右向左扫描表达式,遇到数字,直接将数字压入一个栈中,遇到运算符时,弹出栈顶的两个数,用运算符对他们进行算数运算,并将计算结果再次压入栈中,重复这个过程直到扫描完成整个表达式,最后留在栈中的数字即为最终的运算结果。
例如之举例的前缀表达式:- * + 3 4 5 6
- 首先右边全是数字,直接按照从右至左的顺序压入栈中
- 继续扫描,遇到运算符 + 号,此时弹出栈顶的 3 和 4,进行加法运算,并将结果 7 放入栈中
- 接下来是运算符 * 号,此时弹出栈顶的 7 和 5,进行乘法运算,并将结果 35 放入栈中
- 最后是运算发 - 号,此时淡出栈顶的 35 和 6,进行减法运算,注意:此时要按照栈先入后出的原则,35 后入栈,比 6 先出,所以,35 - 6 = 29,最后将 29 放入栈中,此时表达式扫描完毕,栈中最后的数字即为结果
后缀表达式(逆波兰表达式)
算数表达式解释:百度百科
后缀表达式求值思路:
遇到数字,将数字压入栈中,遇到符号时,弹出栈顶两个数用改运算符做计算(计算顺序:次顶元素 运算符 栈顶元素),然后将计算结果入栈,重复以上操作直到表达式扫描完成,最后栈中的数字即为表达式结果。
如:( 3 + 4 ) * 5 - 6 的后缀表达式为 3 4 + 5 * 6 -
- 将 3 和 4 压入栈中
- 扫描到 + 号,弹出 4 和 3(4为栈顶元素,3为次顶元素),计算加法,结果为 7,压入栈中
- 扫描到 5,压入栈中
- 扫描到 *,弹出 5 和 7,计算乘法,结果 35 压入栈中
- 扫描到 6,压入栈中
- 扫描到 -,弹出 6,计算 35 - 6,结果 29 压入栈中
逆波兰计算器实现
支持小括号和多位整数简单计算的计算器
实现思路答题分为两部分:
- 中缀表达式转后缀表达式
- 根据后缀表达式计算出结果
中缀表达式转后缀表达式规则
中缀转后缀表达式规则我们不需要更多的探讨,因为属于发明算法,我们现在只关心怎么用这个算法就好了,规则如下:
- 1.初始化两个栈:运算符栈 s1 和结果栈 s2
- 2.从左至右扫描表达式
- 3.遇到数字时,直接压入 s2
- 4.遇到符号时,需要比较其与 s1栈顶符号的优先级,满足如下规则
4.1 如果s1为空,或者s1栈顶运算符为 ( | 直接将此符号入栈s1 |
4.2 如果该符号比栈顶运算符优先级高 | 直接将此符号入栈s1 |
4.3 如果不满足 4.1 和 4.2 规则 | 将s1栈顶的运算符弹出压入s2中,再次根据 4.1 判断 |
- 5.遇到括号时,满足如下规则
如果是 ( | 直接压入s1 |
如果是 ) | 依次弹出s1栈顶的运算符,并将这些运算符压入s2,直到遇到 ( 为止,此时将这一对 () 丢弃 |
- 6.重复 2 到 5的判断,直到表达式最右边
- 7.将s1中剩余的运算符依次弹出并压入s2
- 8.依次弹出s2中的元素,并按照结果的倒叙排列,即为得到的后缀表达式
代码实现
package com.leolee.dataStructure.stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* @ClassName ReversePolishNotation
* @Description: 逆波兰表达式
* @Author LeoLee
* @Date 2020/9/17
* @Version V1.0
**/
public class ReversePolishNotation {
/*
* 功能描述: <br> 对逆波兰表达式进行“格式处理”,转为List
* 〈〉
* @Param: [reversePolishNotationExpressions]
* @Return: java.util.List<java.lang.String>
* @Author: LeoLee
* @Date: 2020/9/17 18:08
*/
public static List<String> getListString(String reversePolishNotationExpressions) {
String[] stringArray = reversePolishNotationExpressions.split(" ");
List<String> list = new ArrayList<String>();
for (String s : stringArray) {
list.add(s);
}
return list;
}
/*
* 功能描述: <br> 计算波兰表达式
* 〈〉
* @Param: [list] 后最表达式对应的list
* @Return: int
* @Author: LeoLee
* @Date: 2020/9/17 18:33
*/
public static int calculate (List<String> list) {
Stack<String> stack = new Stack<String>();
for (String item : list) {
//判断字符是数字还是符号
if (item.matches("\\d+")) {
//匹配数字直接入栈
stack.push(item);
} else {
//如果是符号,贼需要弹出两个元素进行运算
int num2 = Integer.valueOf(stack.pop());//栈顶元素
int num1 = Integer.valueOf(stack.pop());//次顶元素
int result = 0;
switch (item.charAt(0)) {
//计算顺序:次顶元素 运算符 栈顶元素
case '+' :
result = num1 + num2;
stack.push(String.valueOf(result));
break;
case '-' :
result = num1 - num2;
stack.push(String.valueOf(result));
break;
case '*' :
result = num1 * num2;
stack.push(String.valueOf(result));
break;
case '/' :
result = num1 / num2;
stack.push(String.valueOf(result));
break;
default:
throw new RuntimeException("不支持的运算符");
}
}
}
return Integer.valueOf(stack.pop());
}
/*
* 功能描述: <br> 中缀表达式转换为 list,主要是为了处理多位数
* 〈〉
* @Param: [normalExpression] 中缀表达式
* @Return: java.util.List<java.lang.String>
* @Author: LeoLee
* @Date: 2020/9/17 21:54
*/
public static List<String> normalExpressionToList (String normalExpression) {
List<String> list = new ArrayList<String>();
char c;//用于转存每一个字符
String str;//用于多位数拼接
for (int i = 0; i < normalExpression.length(); i++) {
c = normalExpression.charAt(i);
//判断是数字还是符号
if (String.valueOf(c).matches("\\d+")) {//数字处理
str = "";
//0~9 对应的 ASC码为 48~57,不在该范围则跳出循环
while (i < normalExpression.length() && (c = normalExpression.charAt(i)) >= 48 && (c = normalExpression.charAt(i)) <= 57) {
str += c;
i++;
}
i--;
list.add(str);
} else {//符号处理
list.add(String.valueOf(c));
}
}
return list;
}
/*
* 功能描述: <br> 中缀表达式字符list 转 后缀表达式字符list
* 〈〉
* @Param: [sourceList] 中缀表达式字符list
* @Return: java.util.List<java.lang.String> 后缀表达式字符list
* @Author: LeoLee
* @Date: 2020/9/17 22:10
*/
public static List<String> getReversePolishNotationList (List<String> sourceList) {
Stack<String> s1 = new Stack<String>();//符号栈
//Stack<String> s2 = new Stack<String>();//结果栈
//由于s2栈在整个处理过程中不需要pop(),只会压入元素,最后取出结果还要倒叙输出,所以在这里使用List
List<String> s2 = new ArrayList<String>();
for (String item : sourceList) {
if (item.matches("\\d+")) {//如果是一个数字则入s2
s2.add(item);
} else if ("(".equals(item)) {//如果是 (,直接压入s1
s1.push(item);
} else if (")".equals(item)) {//如果是 ) 依次弹出s1栈顶的运算符,并将这些运算符压入s2,直到遇到 ( 为止,此时将这一对 () 丢弃
while (!s1.peek().equals("(")) {//peek()只查看栈顶元素,并不弹出
s2.add(s1.pop());
}
s1.pop();//丢弃s1中的 (
} else {
//4.1 如果s1为空,或者s1栈顶运算符为 ( 直接将此符号入栈s1
//4.2 如果该符号比栈顶运算符优先级高 直接将此符号入栈s1
//4.3 如果不满足 4.1 和 4.2 规则 将s1栈顶的运算符弹出压入s2中,再次根据 4.1 判断
while (s1.size() != 0 && OperatorPriority.getValue(s1.peek()) >= OperatorPriority.getValue(item)) {//即4.3的判断条件,item小于等于栈顶运算符的优先级
s2.add(s1.pop());
}
s1.push(item);
}
}
//将s1中剩余的运算符依次弹出并压入s2
while (s1.size() != 0) {
s2.add(s1.pop());
}
return s2;
}
//运算符优先级枚举类
enum OperatorPriority {
DIVIDE("/", 2),
MULTIPLY("*", 2),
ADD("+", 1),
SUBTRACT("-", 1)
;
private String key;
private int val;
private OperatorPriority (String key, int val) {
this.key = key;
this.val = val;
}
public static int getValue (String key) {
ReversePolishNotation.OperatorPriority[] operations = ReversePolishNotation.OperatorPriority.values();
for (int i = 0; i < operations.length; i++) {
if (operations[i].getKey().equals(key)) {
return operations[i].getVal();
}
}
//匹配不到则返回0
return 0;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public int getVal() {
return val;
}
public void setVal(int val) {
this.val = val;
}
}
public static void main(String[] args) {
String normalExpression = "1+(30*3)/2+23-2";
List<String> list = ReversePolishNotation.normalExpressionToList(normalExpression);
List<String> reversePolishNotationList = ReversePolishNotation.getReversePolishNotationList(list);
int result = ReversePolishNotation.calculate(reversePolishNotationList);
System.out.println("1+(30*3)/2+23-2=" + result);
}
}