一、中缀表达式
1、中缀表达式就是常见的运算表达式,如(3+4)×5-6
2、中缀表达式的求值是我们人最熟悉的,但是对计算机来说却不好操作(前面我们讲的案例就能看的这个问题),因此,在计算结果时,往往会将中缀表达式转成其它表达式来操作(一般转成后缀表达式.)
中缀表达式的计算机求值
使用栈实现综合计算器的计算 思路
(1)通过一个 index 值(索引),来遍历我们的表达式
(2) 如果我们发现是一个数字, 就直接入数栈
(3)如果发现扫描到是一个符号, 就分如下情况
1) 如果发现当前的符号栈为 空,就直接入栈
2) 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符, 就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到结果,入数栈,然后将当前的操作符入符号栈, 如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈.
(4) 当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并运行.
(5)最后在数栈只有一个数字,就是表达式的结果
二、前缀表达式
前缀表达式又称波兰式,其运算符在操作数之前,转化如下:
(3+4)*5-6 转化为:- * + 3 4 5 6
前缀表达式的计算机求值
从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 和 次顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果
三、后缀表达式
1、后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后
2、举例说明: (3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 –
3、转化规则
正常的表达式 | 逆波兰表达式 |
---|---|
a+b | a b + |
a+(b-c) | a b c - + |
a+(b-c)*d | a b c – d * + |
a+d*(b-c) | a d b c - * + |
a=1+3 | a 1 3 + = |
后缀表达式的计算机求值
从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素 和 栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果
四、中缀表达式转成后缀表达式
步骤
1、初始化两个栈:运算符栈s1和储存中间结果的栈s2;
2、从左至右扫描中缀表达式;
3、遇到操作数时,将其压s2;
4、遇到运算符时,比较其与s1栈顶运算符的优先级:
(1)如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
(2)否则,若优先级比栈顶运算符的高,也将运算符压入s1;
(3)否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较
5、遇到括号时:(1)如果是左括号“(”,则直接压入s1(2)如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
6、重复步骤2至5,直到表达式的最右边
7、将s1中剩余的运算符依次弹出并压入s2
8、依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
中缀表达式转化为后缀表达式
“1+((2+3)×4)-5” 转换后为: “1 2 3 + 4 × + 5 –”
转化过程
扫描到的元素 | s2(栈底->栈顶) | s1 (栈底->栈顶) | 说明 |
---|---|---|---|
1 | 1 | 空 | 数字,直接入栈 |
+ | 1 | + | s1为空,运算符直接入栈 |
( | 1 | + ( | 左括号,直接入栈 |
( | 1 | + ( ( | 同上 |
2 | 1 2 | + ( ( | 数字 |
+ | 1 2 | + ( ( + | s1栈顶为左括号,运算符直接入栈 |
3 | 1 2 3 | + ( ( + | 数字 |
) | 1 2 3 + | + ( | 右括号,弹出运算符直至遇到左括号 |
× | 1 2 3 + | + ( × | s1栈顶为左括号,运算符直接入栈 |
4 | 1 2 3 + 4 | + ( × | 数字 |
) | 1 2 3 + 4 × | + | 右括号,弹出运算符直至遇到左括号 |
- | 1 2 3 + 4 × + | - | -与+优先级相同,因此弹出+,再压入- |
5 | 1 2 3 + 4 × + 5 | - | 数字 |
到达最右端 | 1 2 3 + 4 × + 5 - | 空 | s1中剩余的运算符 |
五、逆波兰计算器
计算过程
1、将中缀表达式转化为后缀表达式
2、利用后缀表达式进行计算
实现代码
public class PolandNotation {
public static void main(String[] args) {
/**
* 中缀转后缀,并计算
* 1. 1+((2+3)*4)-5 转成 1 2 3 + 4 * + 5 -
* 2. 将中缀表达式转成对应的list,方便
* 3. 将中缀表达式list转成后缀表达式list 即:[1,+,(,(,2,+,3,),*,4,),-,5] => [1,2,3,+,4,*,+,5,-]
*/
String expresstion="1+((2+3)*4)-5";
List<String> infixExpressionList = toInfixExpressionList(expresstion);
System.out.println("中缀表达式对应的List="+infixExpressionList);
List<String> suffixExpressionList = parseSuffixExpressionList(infixExpressionList);
System.out.println("后缀表达式对应的List="+suffixExpressionList);
System.out.printf("%s=%d",expresstion,calculate(suffixExpressionList));
}
//将后缀表达式的数据和运算符依次放入到ArrayList
public static List<String> getListString(String suffixExpression){
String[] split=suffixExpression.split(" ");
List<String> list=new ArrayList<>();
for (String ele:split){
list.add(ele);
}
return list;
}
//计算
public static int calculate(List<String> items){
Stack<String> stack=new Stack<String>();
for (String item:items){
if(item.matches("\\d+")){//匹配的是多位数
stack.push(item);
}else {
int num2 = Integer.parseInt(stack.pop()) ;
int num1 = Integer.parseInt(stack.pop()) ;
int res=0;
if(("+").equals(item)){
res=num1 +num2;
}else if(("-").equals(item)){
res=num1-num2;
}else if(("*").equals(item)){
res=num1*num2;
}else if(("/").equals(item)){
res=num1/num2;
}else {
throw new RuntimeException("运算符错误");
}
stack.push(""+res);
}
}
//最后留在栈中的数据就是运算结果
return Integer.parseInt(stack.pop());
}
//中缀转后缀
//将中缀表达式转成对应的List str="1+((2+3)*4)-5"
public static List<String> toInfixExpressionList(String s){
List<String> list=new ArrayList<String>();
int i=0;
String str;
char c;
do{
if((c=s.charAt(i))<48||(c=s.charAt(i))>57){//如果是一个非数字,加到ls
list.add(""+c);
i++;
}else {//如果是一个数字
str="";
while (i<s.length() && (c=s.charAt(i))>=48 && (c=s.charAt(i))<=57){
str+=c;
i++;
}
list.add(str);
}
}while (i<s.length());
return list;
}
//将中缀表达式转化为后缀表达式
public static List<String> parseSuffixExpressionList(List<String> list){
//符号栈
Stack<String> s1=new Stack<String>();
//s2只有添加,没有pop,加上如果用栈还得逆序输出,所以这里直接用list
List<String> s2=new ArrayList<String>();
for (String item:list){
if(item.matches("\\d+")){
s2.add(item);
}else if(item.equals("(")){
s1.push(item);
}else if(item.equals(")")){
while (!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop();//去掉左括号
}else {
//是运算符 比较优先级
while (s1.size()!=0 && Operation.getValue(s1.peek())>=Operation.getValue(item)){
s2.add(s1.pop());
}
s1.push(item);
}
}
//将s1中剩余的运算符依次放到s2中
while (s1.size()!=0){
s2.add(s1.pop());
}
return s2;
}
}
class Operation{
private static int ADD=1;
private static int SUB=1;
private static int MUL=2;
private static int DIV=2;
//返回优先级
public static int getValue(String operagion){
int result=0;
switch ( operagion){
case "+":
result=ADD;
break;
case "-":
result=SUB;
break;
case "*":
result=MUL;
break;
case "/":
result=DIV;
break;
default:
System.out.println("不存在该运算符");
break;
}
return result;
}
}
该代码实现的逆波兰计算器只支持整数计算哦~