从三种表达式说起
前缀表达式介绍
1.前缀表达式(波兰表达式)
*前缀表达式又称波兰式,前缀表达式的运算符位于操作数之前
*:(3+4)*5-6(中缀表达式) 对应的前缀表达式就是- X +3 4 5 6
前缀表达式在计算机中求值:
从右到左进行表达式的扫描,遇到数字时,将数字压入堆栈。遇到运算符时,弹出栈顶的两个数,用运算符对它们做计算(栈顶元素和栈底元素),然后将结果入栈
重复上述结果得到最终的结果
中缀表达式介绍
*中缀表达式就是常见的运算表达式 例如:(3+4)*5+6
*但是对于计算机来说,直接操作中缀表达式并不好操作,往往会把中缀表达式变成其他的(一般变为后缀表达式)
后缀表达式介绍
后缀表达式又称逆波兰表达式,和前缀表达式类似,只是运算符位于操作数之后
类似: (3+4)5-6–>34+56-
后缀表达式在计算机中的求值:
从左到右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符的时候,弹出栈顶两个数进行运算 将结果入栈 重复上述结果得到最终的结果
如何写一个简单的逆波兰计算器
part 1.有栈的基础知识(为了方便在这里使用系统的Stack类)
part 2.知道如何将中缀表达式转换为后缀表达式()
part 3.知道如何进行后缀表达式的计算
代码效果: 输入一串中缀表达式,通过算法(1)转换为后缀表达式,然后通过算法(2)计算出结果
part1: 可以看之前一篇文章,总结了一下栈的基础
part2:中缀表达式转为后缀表达式
思路:
如何将中缀表达式转成后缀表达式?
具体步骤如下:
1.初始化两个栈,运算符栈s1和储存中间结果的栈s2(出于实际情况考虑,可以使用ArrayList)
2.从左到右扫描中缀表达式
3.遇到操作数的压s2栈
4.遇到运算符,则比较和s1栈顶运算符的优先级
4-1:如果s1为空,或者栈顶运算符为左括号'(',则直接压s1
4-2:否则,若优先级比栈顶运算符高,直接压s1
4-3:否则,将s1栈顶的运算符弹出并压入s2,重复s1比较
5.遇到括号时:
5-1:如果是左括号,直接压入s1
5-2:如果是右括号,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
6.重复步骤2-5 直到扫描到表达式的最右边
7.将s1中剩余的运算符依次弹出并压入s2
8.依次弹出s2中的元素并输出,结果的逆序就是中缀表达式对应的后缀表达式
ep:中缀表达式: 1+((2+3)*4)-5 如何进行转变为后缀
1.初始化两个栈 s1(符号栈) s2(操作数栈)
2.开始进行扫描
...
s1: + ( ( + )
s2: 1 2 3
//已经扫描到了3后面的右括号
//此时出现第一个变化 注:括号不算是运算符
s1: + (
s2: 1 2 3 +
//继续扫描
s1: + ( *
s2: 1 2 3 + 4
//由于 * 的优先级比+高 所以可以直接进行压栈
//继续扫描
s1: +
s2: 1 2 3 + 4 *
//这时候已经扫描到了4后的括号
//继续扫描
s1: + -
s2: 1 2 3 + 4 *
!!! 由于+ - 的优先级一致 弹出当前s1栈顶 并且压入s2
s1: -
s2: 1 2 3 + 4 * +
//继续扫描
s1: -
s2: 1 2 3 + 4 * + 5
//已经扫描到了最右边,将s1剩下的运算符弹出 并且压入s2中
s1: null
s2: 1 2 3 + 4 * + 5 -
//最后逆序输出 得到最后的答案
中缀表达式: 1+((2+3)*4)-5
后缀表达式: 1 2 3 + 4 * + 5 -
代码实现:
//将中缀表达式转化为后缀表达式
public static List<String> parseSuffixExpressionList(List<String> ls){
//定义
Stack<String> s1=new Stack<String>();//符号栈
//由于在s2栈中,在整个转换过程中,没有pop的操作,而且后面还需要逆序输出。因此比较麻烦,所以不使用栈,直接使用List进行替代
List<String> s2=new ArrayList<String>();
//遍历ls
for (String item : ls) {
//如果是一个数 加入到s2
if(item.matches("\\d+")){
s2.add(item);
}
//如果是左括号,就直接入到s1
else if(item.equals("(")) {
s1.push(item);
}
//如果是右括号 依次弹出s1栈顶的运算符,并且压入s2,直到遇到左括号为止,此时将一对括号丢弃
else if(item.equals(")")){
while(!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop();//将左括号弹出s1【消除括号】
}
//考虑符号的优先级问题
else{
//当item的优先级小于等于s1栈顶运算符的优先级,将s1栈顶的运算符弹出加入s2中
//缺少方法来判断优先级
while(s1.size()!=0 && Operation.getValue(s1.peek())>=Operation.getValue(item)){
s2.add(s1.pop());
}
//遍历后还需要将item压入栈中
s1.push(item);
}
//遍历已经结束了 将s1中剩余运算符加入s2中
}
while(s1.size()!=0){
s2.add(s1.pop());
}
//因为是存放到list中,所以无需进行逆转
return s2;
}
part3 基于后缀表达式的计算器实现:
思路:
从左到右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符的时候,弹出栈顶两个数进行运算 将结果入栈 重复上述结果得到最终的结果
代码实现:
//开始对逆波兰表达式的运算
public static int calculate(List<String> ls) {
//只需要一个栈即可
Stack<String> stack = new Stack<>();
for (String item : ls) {
//使用正则表达式判断是否遍历的是数(任意数字均可)!!!
if (item.matches("\\d+")) {
//匹配的是多位数
stack.push(item);
} else {
//运算符(从栈中弹出两个数进行运算,再入栈)
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
//开始运算
if (item.equals("+")) {
res = num1 + num2;
} else if (item.equals("-")) {
res = num1 - num2;
} else if (item.equals("*")) {
res = num1 * num2;
} else if (item.equals("/")) {
res = num1 / num2;
} else {
throw new RuntimeException("符号错误");
}
//将运算后的值入栈
stack.push(String.valueOf(res));
}
}
return Integer.parseInt(stack.pop());
}
注:从这里和之前的中缀表达式进行对比,也看得出使用后缀表达式再进行计算非常简单(使用一个栈操作即可)
完整代码可以去我的github上下载:https://github.com/Lzin/stack