逆波兰
逆波兰表达式又叫做后缀表达式。逆波兰表示法是波兰逻辑学家J・卢卡西维兹(J・ Lukasewicz)于1929年首先提出的一种表达式的表示方法 。后来,人们就把用这种表示法写出的表达式称作“逆波兰表达式”。逆波兰表达式把运算量写在前面,把算符写在后面。
波兰即前缀表达式,逆波兰即后缀表达式,中缀表达式是最常见的即平时我们所见的表达式:
下面是三种表达式样式
中缀表达式:10+ (5 + 15) × 2 - 6
前缀表达式:- + 10 * + 5 15 2 6
后缀表达式:10 5 15 + 2 * + 6 -
前缀表达式是运算符在前数字在后,后缀表达式数字在前运算符在后
下文主要是写了后缀表达式的三个基本方法,包括字符串转中缀以及中缀转后缀,后缀表达式计算。
将运算符放入一个栈,将数字放入另一个栈中,由于数字栈没有出栈操作,所以可以放入链表中
中缀转后缀思路:
将字符串转换为链表后,遍历链表
1 当遍历值是整数时,直接放入链表中,否则2
2当遍历值是运算符时,如果当前栈为空直接放入 否则3
3遍历值是运算符“(”时直接放入栈中,否则4
4 遍历值是运算符“)”时,执行出栈并加入存放数字的链表中操作,直到出栈预算符为“(”停止,并将“(”出栈
5 遍历值是运算符是其他时,此时需要判断运算符的优先级,如果当前运算符小于或等于栈顶元素时,执行出栈并放入链表中的操作,否则6
6 将运算符压入栈中
将上述步骤遍历操作执行完成后,将栈中剩下的所有运算符出栈存放到链表中,此时的链表即转换好的后缀表达式。
下面是后缀表达式计算思路:
首先创建一个栈用于存放遍历数据
计算过程很简单,首先遍历后缀表达式链表:
1当遍历值是数字时直接压入栈中
2当遍历值是字符时,出栈两个数字,并进行该运算符的运算操作,并将运算结果压入栈中。
当遍历完成后,栈中值即后缀表达式的运算结果
中缀转前缀
这个跟转后缀类似,不同的是它是从右往左遍历也就是链表的倒叙遍历,同转后缀相似的方法,但是它的保存数字的不应该是链表,而是栈,按栈的出栈顺序即是前缀表达式的顺序,后缀用了链表是因为后缀中数据处理结束后,后缀表达式的顺序是(如果用栈的化是栈出栈的反向序列)所以采用了链表存储,这样就不用反序列操作,直接正向遍历即可
下面是中缀转后缀代码实现:
import java.util.ArrayList;
import java.util.Stack;
public class ReversePolishDemo {
private static int ADD = 0;
private static int SUB = 0;
private static int MUL = 1;
private static int DIV = 1;
public static void main(String[] args) {
ReversePolishDemo rpd = new ReversePolishDemo();
String s = "1+((2+3)*4)-5";
ArrayList<String> infixList = rpd.toinfixList(s);
ArrayList<String> postfixList = rpd.toPostfixExpressionList(infixList);
System.out.println(postfixList.toString());
int result = rpd.colculate(postfixList);
System.out.println("计算结果是:" + result);
}
//字符串转成链表方便处理
public ArrayList<String> toinfixList(String s) {
ArrayList<String> infixList = new ArrayList();
String num = "";
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) < 48 || s.charAt(i) > 57) {//48-57为数字字符对应的asc范围
if ("" != num) {
infixList.add(num);
num = "";
}
infixList.add((String)s.substring(i, i+1));
} else {
num = num + s.charAt(i);
}
}
if ("" != num) {
infixList.add(num);
}
System.out.println(infixList.toString());
return infixList;
}
public ArrayList<String> toPostfixExpressionList(ArrayList<String> infixList){
ArrayList<String> postfixList = new ArrayList();
Stack operators = new Stack();
for(int i = 0;i<infixList.size();i++){
if(infixList.get(i).matches("\\d+")){//如果是数字
postfixList.add(infixList.get(i));
}else{
if(operators.size() == 0){//如果栈为空
operators.push(infixList.get(i));
}else if("(".equals(infixList.get(i))){//如果栈为(
operators.push(infixList.get(i));
}else if(")".equals(infixList.get(i))){//如果栈为)
while(!"(".equals(operators.peek())){
postfixList.add((String) operators.pop());
}
operators.pop();//将(出栈
}else{
while(operators.size()>0 && getPriority(infixList.get(i))<= getPriority(operators.peek().toString())){
postfixList.add((String) operators.pop()); //小于或等于栈中优先级则将操作符存放至链表中
}
operators.push(infixList.get(i));
}
}
}
while(operators.size()>0){
postfixList.add((String) operators.pop());
}
return postfixList;
}
public int getPriority(String s){
switch(s){
case "+":
return ADD;
case "-":
return SUB;
case "*":
return MUL;
case "/":
return DIV;
}
return -999;
}
public int colculate(ArrayList<String> postfixList){
Stack list = new Stack();
for(String s:postfixList){
if(s.matches("\\d+")){
list.push(Integer.valueOf(s));//是数字直接压栈
}else{
int num1 = (int) list.pop();//是字符出栈两个数字
int num2 = (int) list.pop();
if(s.equals("+")){
list.push(num1+num2);//运算结果压入栈中
}else if(s.equals("-")){
list.push(num2-num1);
}else if(s.equals("*")){
list.push(num1*num2);
}else if(s.equals("/")){
list.push(num2/num1);
}
}
}
return (int) list.pop();
}
}