总述
本篇文章主要介绍栈这种数据结构在计算器的实现中的应用,核心思想即为如何实现中缀表达式转换为后缀表达式
思路分析
- 基本概念
- 我们都知道1个表达式是由运算符、数字以及括号组成。例如:1-(2+3)*5,在计算的时候,我们首先计算括号中的内容,然后根据运算符的优先级的高低去进行相应的计算。我们平常见到的这种表达式即为中缀表达式
- 什么是后缀表达式?后缀表达式又被称为逆波兰表达式,是一种没有括号,严格遵循从左到右进行运算的表达式。对于1-(2+3)*5这个中缀表达式,对应的后缀表达式即为1 2 3 + 5 * -,后缀表达式的特点即为从左至右遇到运算符就将离该运算符最近的两个数进行运算,即
- 从左至右 1 2 3,然后遇到+,则对2,3进行加法运算得到5
- 然后继续向右遇见5 和 * ,然后对 5 5进行乘法运算得到25
- 最后遇到 - ,对1 25进行减法运算得到-24
- 中缀表达式明显是符合我们思考的方式,但对于计算机而言,它无法很高效的进行"摘取"运算,它只能规矩的从左到右进行运算,因此想要实现计算器的功能,中缀表达式转为后缀表达式的实现是十分必要的
- 栈:栈是一种先入后出的有序列表,是限制线性表中元素的插入与删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端为变化的一端,称为栈顶(top****),另一端为固定的一端,称为栈底(bottom)
- 思路分析
-
为什么要用栈去实现表达式的计算呢?前面我们已经说过后缀表达式的作用,因此我们在对后缀表达式进行遍历时肯定是从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算,并将结果入栈,重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果。图解:
-
中缀表达式转为后缀表达式思路
- 初始化两个栈,运算符栈s1和数字栈s2
- 从左至右扫面中缀表达式
- 遇到操作数时,将其压s2
- 遇到运算符时,比较其与s1栈顶运算符的优先级
- 如果s1为空,或栈顶元素为’(‘,则直接将运算符入栈s1
- 如果优先级比栈顶运算符高,直接将运算符入栈s1
- 否则将s1中的栈顶元素弹出并加入到s2中,回到4.1与s1中新的栈顶元素比较
- 遇到括号时
1. 左括号:直接压入s1
2. 右括号,依次弹出s1栈顶的运算符并加入s2,直到遇到左括号为止,此时将这一对小括号丢弃 - 重复2~5,直到表达式的最右边
- 将s1中剩余的运算符依次弹出并压入s2
- 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
-
图解:(以1-(2+3)*5这个表达式进行图解)
-
相关代码片段
/**
* 中缀表达式转后缀表达式
* @author qiu
* @version 1.8.0
*/
public class Suffix {
public static void main(String[] args) {
String s = "1+((2+3)*4)-5";
System.out.println(transform(s));
System.out.println(transform1(transform(s)));
}
/**
* 中缀表达式转为后缀表达式
*/
public static List<String> transform1(List<String> ls){
Stack<String> s1 = new Stack<>();
List<String> s2 = new ArrayList<>();
for(String item:ls){
//多位数直接加入到集合:正则表达式
if(item.matches("\\d+")){
s2.add(item);
}else if("(".equals(item)||s1.empty()){
//左括号直接入符号栈
s1.push(item);
}else if(")".equals(item)){
//右括号则将栈中符号元素加入到集合中直到运算符栈顶元素为左括号
while(!"(".equals(s1.peek())){
s2.add(s1.pop());
}
//记得将左括号去掉
s1.pop();
}else{
//运算符则与栈顶元素进行比较若为空或左括号则直接入栈
//若该运算符有优先级高于栈顶元素的优先级则直接入栈
//优先级低于则一直弹出栈顶元素直到不满足优先级低于的条件后再将运算符入栈
while(!s1.empty()&&operation(item)<=operation(s1.peek())){
s2.add(s1.pop());
}
s1.push(item);
}
}
while(!s1.empty()){
s2.add(s1.pop());
}
return s2;
}
public static int operation(String s){
if("+".equals(s)||"-".equals(s)){
return 1;
}
if("*".equals(s)||"/".equals(s)){
return 2;
}
return 0;
}
/**
* 将字符串形式的中缀表达式转换为集合存储
* @param s
* @return
*/
public static List<String> transform(String s){
List<String> ls = new ArrayList<>();
int index = 0;
char ch = ' ';
String str = "";
while(true){
if((ch=s.charAt(index)) <48||(ch = s.charAt(index))>57){
//非数字直接入集合
ls.add(ch+"");
index++;
}else{
//是数字要考虑多位数拼接问题
while(index < s.length()&&(ch =s.charAt(index))>=48&&(ch = s.charAt(index))<=57){
str+=ch;
index++;
}
ls.add(str);
str = "";
}
if(index == s.length()){
break;
}
}
return ls;
}
}