栈的应用(中缀表达式转逆波兰表达式)
栈的应用(中缀表达式转逆波兰表达式)
思路:
因为中缀表达式对于操作符的优先级很不好计算,就将中缀转成计算机更容易识别的后缀表达式
中缀表达式转后缀表达式的思路步骤分析:
- 初始化两个栈:运算符栈s1和存储中间结果的栈s2
- 从左至右扫描中缀表达式
- 遇到操作数时,将其压入s2
- 遇到运算符时:
1.如果s1空,或栈顶运算符为左括号"(",则直接将此运算符入s1
2.如果s1栈顶为运算符,并且优先级比运算符高,也将运算符压入s1
3.否则,将s1栈顶运算符弹出并压入s2,再次转到 4.1 步骤,判断新的栈顶符号,是比较优先级还是直接入栈 - 遇到括号时:
1.如果是左括号 "(" ,则直接压入s1
2.如果是右括号 ")", 则遍历栈中左括号 “(” 上面的所有运算符,依次弹出压入s2中
然后将左括号弹出,将这一对括号丢弃 - 重复步骤2到5,直至表达式的最右边
- 将s1中剩余的运算符依次弹出压入s2
- 依次弹出s2中的元素并输出,后缀表达式就是输出结果的逆序
代码实现
处理中缀表达式格式
/**
* 用于将中缀表达式转成数字,操作符,括号都分开存储的List集合
* @param s 中缀表达式
* @return 转换后的List集合
*/
public static List<String> toInfixExpressionList(String s) {
//先定义一个List,存放中缀表达式 对应的内容
List<String> ls = new ArrayList<>();
int i = 0; //字符指针索引,用于遍历 中缀表达式字符串
String str = ""; //对多位数的拼接
char c; //每遍历到一个字符,就放入到c
while (i != s.length()) {
//如果c是一个非数字,则加入到ls
c = s.charAt(i);
if (c < 48 || c > 57) { // 0 -> ASCILL 48 10 -> ASCILL 58
//判断为操作符
ls.add(c + "");
i++;
} else {
//判断为数字后,紧跟着向后遍历,碰到数字就拼接,不是数字就停止遍历,然后将拼接的多位数加入List
while(i<s.length()&& (c=s.charAt(i))>=48 && (c=s.charAt(i))<=57){
//拼接多位数
str += c;
i++;
}
ls.add(str);
str="";
}
}
return ls;
}
中缀转后缀
/**
* 用于将中缀表达式转换成后缀表达式
*
* @param inffixList 中缀表达式的List
* @return 后缀表达式的List
*/
public static List<String> parseSuffixExpressionList(List<String> inffixList){
Stack<String> stack = new Stack<>();// 符号栈
/*
这里不使用栈来存储表达式转换结果集是因为,使用栈存储,始终没有出栈操作
并且需要从栈底向栈顶,即逆序输出存入的结果集,使用List就可以直接顺序输出
*/
List<String> ls = new ArrayList<>();// 结果集
for (String s : inffixList) {
if(s.matches("\\d+")){// 判断是数字,直接加入结果集
ls.add(s);
}else if(s.equals("(")){// 判断是左括号,直接入栈
stack.push(s);
}else if(s.equals(")")){// 判断是右括号,从符号栈中寻找一个左括号,并消除
// 如果中间有运算符号,直接加入结果集
while(!stack.peek().equals("(")){
//遍历,左右括号之间的运算符
ls.add(stack.pop());
}
//弹出 ( 左括号,消除
stack.pop();
}else{// 判断是操作符
//当s的优先级小于等于栈顶运算符优先级时,将栈顶运算符弹出,加入ls 结果集,再与新的栈顶运算符比较优先级
boolean loop = false; //表示是否需要继续比较运算符优先级
do{
//符号栈不为空,才比较优先级
if(stack.size() == 0 || stack.peek().equals("(")){
stack.push(s);
//关闭循环,只有出现遍历的 s运算符优先级小于等于栈顶运算符 时才开启循环,其他情况不需要循环
loop = false;
}else if(Operation.getPriority(s) > Operation.getPriority(stack.peek())){
//s 遍历的运算符优先级比栈顶运算符优先级大,直接入符号栈
stack.push(s);
loop = false;
}else{
//s 遍历的运算符优先级小于等于栈顶运算符优先级,弹出栈顶运算符号加入ls
//重复循环,判断新的栈顶是否为空或者比较优先级,进行下一步操作
loop = true;
ls.add(stack.pop());
}
}while (loop);
}
}
//遍历完成后,如果符号栈内不为空,则依次弹出加入到ls中
while(!stack.isEmpty()){
ls.add(stack.pop());
}
return ls;
}
运算符类,用于返回运算符优先级
class Operation{
private final static int ADD_SUB = 1;
private final static int MUL_DIV = 2;
public static int getPriority(String oper){
switch (oper){
case "+":
case "-":
return ADD_SUB;
case "*":
case "/":
return MUL_DIV;
default:
throw new RuntimeException("运算符有误!");
}
}
}