逆波兰表达式
1、前言
看这边文章之前,我们首先要知道什么是逆波兰表达式。逆波兰表达式又叫做后缀表达式,是波兰的以为逻辑学家于1929年首先提出的一种表达式的表示方法。逆波兰表达式把运算量写在前面,把运算符写在后面。
我们知道逆波兰表达式又叫做后缀表达式,那么你是不是就有这样的猜想?是不是还有前缀表达式,没错,不仅仅有前缀表达式,还有中缀表达式,而中缀表达式是我们见过最多的一种。
- 前缀表达式:前缀表达式又称波兰式,前缀表达式的运算符位于操作数之前。比如:- × + 3 4 5 6
- 中缀表达式就是常见的运算表达式,如(3+4)×5-6
- 后缀表达式:运算符位于操作数之后,就是 3 4 + 5 × 6 -
逆波兰表达式是一种十分有用的表达式,它将复杂表达式转换为可以依靠简单的操作得到计算结果的表达式
优势:
它的优势在于只用两种简单操作,入栈和出栈就可以搞定任何普通表达式的运算。其运算方式如下:
如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。
2、通过逆波兰表达式计算结果
将中缀表达式:3*(17-15)+18/6转为逆波兰表达式为:3 17 15 - * 18 6 / +
1、从左至右扫描,将3,17,15压入堆栈;
2、遇到-运算符,因此弹出15和17(15为栈顶元素,17为次顶元素),计算出17-15的值,得2,再将2入栈;
3、接下来是×运算符,因此弹出2和3,计算出2 x 3=6,将6入栈;
4、将18 6入栈
5、遇到 / 运算符,弹出6 和18,计算18/6=3,将3入栈,
6、最后是+运算符,计算出6+3的值,即9,由此得出最终结果(此时栈中只有一个数据)。
java代码实现
可以采用一个辅助的栈来实现计算,扫描表达式从左往右进行,如果扫描到数值,则压进辅助栈中,如果扫描到运算符,则从辅助栈中弹出两个数值参与运算,并将结果压进到栈中,当扫描表达式结束后,栈顶的数值就是表达式结果。
//逆波兰表达式
public class ReversePolishNotation {
public static void main(String[] args) {
//中缀表达式:3*(17-15)+18/6的逆波兰表达式如下
String[] notation = {"3","17","15","-","*","18","6","/","+"};
int result = caculate(notation);
System.out.println("逆波兰表达式结果:"+result);
}
/**
* @param notation 逆波兰表达式的数组表示方式
* @return 逆波兰表达式的计算结果
*/
private static int caculate(String[] notation) {
//定义一个栈,用来存储操作数
Stack<Integer> oprands = new Stack<>();
//从左往右遍历逆波兰表达式
for (int i = 0; i < notation.length; i++) {
String curr = notation[i];
//判断当前元素是运算符还是操作数
Integer o1;
Integer o2;
Integer result;
switch (curr){
//如果是运算符,就从栈中弹起两个操作数,完成运算,并把运算完的结果在压入栈中
case "+":
o1 = oprands.pop();
o2 = oprands.pop();
result = o2+o1;
oprands.push(result);
break;
case "-":
o1 = oprands.pop();
o2 = oprands.pop();
result = o2-o1;
oprands.push(result);
break;
case "*":
o1 = oprands.pop();
o2 = oprands.pop();
result = o2*o1;
oprands.push(result);
break;
case "/":
o1 = oprands.pop();
o2 = oprands.pop();
result = o2/o1;
oprands.push(result);
break;
default:
//如果是操作数,就把该操作数放入栈中
oprands.push(Integer.parseInt(curr));
break;
}
}
//得到栈中最后一个元素,就是逆波兰表达式的结果
int result = oprands.pop();
return result;
}
}
运算结果:
结果和我们分析的结果一致。
3、中缀表达式转换为后缀表达式(逆波兰表达式)
中缀表达式转后缀表达式主要用到了栈进行运算符处理,队列进行排序输出,规则为:
1、数字直接入队列
2、运算符要与栈顶元素比较
- 栈为空直接入栈
- 运算符优先级大于栈顶元素优先级则直接入栈
- 小于或等于则出栈入列,再与栈顶元素进行比较,直到运算符优先级小于栈顶元素优先级后,操作符再入栈
3、操作符是 ( 则无条件入栈
4、操作符为 ) ,则依次出栈入列,直到匹配到第一个 ( 为止,此操作符直接舍弃,( 直接出栈舍弃
java代码实现:
/**
* 中缀表达式转为后缀表达式
*/
public class PolishNotation {
public static void main(String[] args) {
String str = "1+((2+3)×4)-5";
String transfer = transfer(str);
System.out.println(transfer);
}
/**
* 将中缀表达式转换为后缀表达式(逆波兰表达式)
* @param express
* @return
*/
public static String transfer(String express){
Stack<String> stack = new Stack<>();
List<String> list= new ArrayList<>();
for (int i=0;i<express.length();i++){
if ((express.charAt(i)+"").matches("\\d")){
list.add(express.charAt(i)+"");
}else if((express.charAt(i)+"").matches("[\\+\\-\\*\\/]")){
//如果stack为空
if (stack.isEmpty()){
stack.push(express.charAt(i)+"");
continue;
}
//不为空
//上一个元素不为(,且当前运算符优先级小于上一个元素则,将比这个运算符优先级大的元素全部加入到队列中
while (!stack.isEmpty()&&!stack.lastElement().equals("(")&&!comparePriority(express.charAt(i)+"",stack.lastElement())){
list.add(stack.pop());
}
stack.push(express.charAt(i)+"");
}else if(express.charAt(i)=='('){
//遇到左小括号无条件加入
stack.push(express.charAt(i) + "");
}else if(express.charAt(i)==')'){
//遇到右小括号,则寻找上一堆小括号,然后把中间的值全部放入队列中
while(!("(").equals(stack.lastElement())){
list.add(stack.pop());
}
//上述循环停止,这栈顶元素必为"("
stack.pop();
}
}
//将栈中剩余元素加入到队列中
while (!stack.isEmpty()){
list.add(stack.pop());
}
StringBuffer stringBuffer = new StringBuffer();
//变成字符串
for (String s : list) {
stringBuffer.append(s);
}
return stringBuffer.toString();
}
/**
* 比较运算符的优先级
* @param o1
* @param o2
* @return
*/
public static boolean comparePriority(String o1,String o2){
return getPriorityValue(o1)>getPriorityValue(o2);
}
/**
* 获得运算符的优先级
* @param str
* @return
*/
private static int getPriorityValue(String str) {
switch(str){
case "+":
return 1;
case "-":
return 1;
case "*":
return 2;
case "/":
return 2;
default:
throw new RuntimeException("没有该类型的运算符!");
}
}
}
输入中缀表达式为:1+((2+3)×4)-5。测试结果如下:
后缀表达式的特点就是计算机运算非常方便,需要用到栈;计算机处理过程只需要顺序读入,如果遇到数字,则放入栈中,如果是运算符,则将两个栈中数字取出进行运算;