中缀表达式和后缀表达式
1. 中缀表达式(栈实现综合计算器)
请输入一个表达式:
计算:722-5+1-5+3-3
思路分析:
- 通过一个index值(索引)来遍历我们的表达式;
- 如果发现是一个数字,就直接入数栈;
- 如果发现扫描到一个符号,就分如下情况;
3.1 如果当前符号栈为空,就直接入栈;
3.2 如果符号栈有操作数,就进行比较,如果当前操作符的优先级小于或则和等于栈中的操作符,就需要从数栈种pop出两个数,从符号栈种pop出一个运算符,进行运算,将得到的结果入数栈,然后将当前的操作符入符号栈;如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈; - 当表达式扫描完毕后,就顺序的从数栈和符号栈中pop出相应的数和符号,并运行;
- 最后在数栈只有一个数字,就是表达式的结果。
程序:
import java.util.Stack;
//中缀表达式四则运算,支持多位数(没有括号和负号)
//两个栈实现 四则运算
public class Calculator {
public static void main(String[] args) {
String str = "30+4/2-2";
Stack<String> digital = new Stack<>();
Stack<String> operator = new Stack<>();
int index = 0;
while(index < str.length()){
String s = str.substring(index,index+1);
String ss = "";//用于数字拼接
if(s.charAt(0) >= '0' && s.charAt(0) <='9'){
//如果是数字,此时不能直接入栈;
//还需要向后再看一位,如果是数字,需要进行拼接
ss = "";//置为空
ss += s;
index++;
while(index < str.length()) {
char tmp = str.substring(index, index + 1).charAt(0);
if (tmp < '0' || tmp > '9'){//不为数字
break;
}else{//为数字
ss += tmp;
}
index++;
}
digital.add(ss);
}else{
if(operator.isEmpty()){//如果为空,则直接入栈
operator.add(s);
index++;
}else{
if(getLevel(s) > getLevel(operator.peek())){//如果高于栈顶的运算符号的优先级,则直接入栈
operator.add(s);
index++;
}else{//否则,数栈pop出两个数,符号pop栈顶运算符号,进行运算
double[] arrs = new double[2];
arrs[0] = Double.valueOf(digital.pop());
arrs[1] = Double.valueOf(digital.pop());
String op = operator.pop();
double res = calculate(arrs, op);//运算
digital.add(String.valueOf(res));//结果再入数栈
operator.add(s);//最后将当前符号入栈
index++;
}
}
}
}
//扫描完毕以后,如果符号栈不为空,执行出栈,运算操作
while(!operator.isEmpty()){
double[] arrs = new double[2];
arrs[0] = Double.valueOf(digital.pop());
arrs[1] = Double.valueOf(digital.pop());
String op = operator.pop();
double res = calculate(arrs, op);
digital.add(String.valueOf(res));
}
System.out.printf("%.3f",Double.valueOf(digital.pop()));
}
public static int getLevel(String s){
int res = -1;
switch(s){
case "+":
case "-":
res = 1;
break;
case "*":
case "/":
res = 2;
break;
}
return res;
}
public static double calculate(double[] arrs, String op){
double res = -1;
switch(op){
case "+":
res = arrs[1] + arrs[0];
break;
case "-":
res = arrs[1] - arrs[0];
break;
case "*":
res = arrs[1] * arrs[0];
break;
case "/":
res = arrs[1] / arrs[0];
break;
}
return res;
}
}
2. 后缀表达式
中缀表达式转后缀表达式思路:
- 初始化两个栈:运算符栈s1和存储中间结果的栈s2
- 从左至右扫描中缀表达式;
- 遇到操作数时,将其压入s2;
- 遇到运算符时,比较其与s1栈顶运算符的优先级
1)如果s1为空 或 栈顶运算符为 “(” ,则直接将此运算符入栈;
2)否则,若优先级比栈顶运算符的高,也将运算符压入s1;
3) 否则,将s1栈顶的运算符弹出,并压入s2中,再次转到4.1与s1中的栈顶运算符相比较; - 遇到括号时:
1)如果是左括号,则直接压入s1
2)如果是右括号,则依次弹出s1栈顶的运算符号,并且压入s2,直到遇到左括号为止,此时将这一对括号丢弃; - 重复步骤2至步骤5,直至表达式的最右边
- 将s1中剩余的运算符依次弹出并压入s2;
- 依次弹出s2中的元素并输出,结果的逆序记为中缀表达式对应的后缀表达式
程序:
public class PolandNotation {
public static void main(String[] args) {
//完成将一个中缀表达式转成后缀表达式的功能
//说明
//1. 1+((2+3)×4)-5 => 转成 1 2 3 + 4 × + 5 –
//2. 因为直接对str 进行操作,不方便,因此 先将 "1+((2+3)×4)-5" =》 中缀的表达式对应的List
// 即 "1+((2+3)×4)-5" => ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
//3. 将得到的中缀表达式对应的List => 后缀表达式对应的List
// 即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] =》 ArrayList [1,2,3,+,4,*,+,5,–]
String s="1+((2+3)*4)-5";
List list1 = toInfixExpressionList(s);
System.out.println(list1);
List list2 = parseSuffixExceptionList(list1);
System.out.println(list2);
}
//方法:将 中缀表达式 转成对应的 List
//s = "1+((2+3)×4)-5";
public static List<String> toInfixExpressionList(String s){
//定义一个List存放字符串中的元素
ArrayList<String> list = new ArrayList<>();
String str; //用于数字拼接
int index = 0; //遍历字符串的索引
while(index < s.length()){
char ch = s.charAt(index);
//判断试字符还是数字
if(ch< '0' || ch > '9'){//是字符,直接存入list
list.add(""+ch);//list中存入字符
index++;
}else{ //是数字,需要考虑拼接
str=""; //先将str 置成"" '0'[48]->'9'[57]
str+=ch;//字符符串拼接
index++;//索引下标
// && 短路运算符 如果第一个不成立,后面的不再进行判断
// 此处应该先判断索引是否超过字符串的长度,否则不能判断字符范围
while(index < s.length() && s.charAt(index) >= '0' && s.charAt(index) <= '9'){
str += s.charAt(index);
index++;
}
list.add(str);//list中存入数字
}
}
return list;
}
/*
* 中缀表达式转后缀表达式思路:
* 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中的元素并输出,结果的逆序记为中缀表达式对应的后缀表达式
*
* */
//即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] =》 ArrayList [1,2,3,+,4,*,+,5,–]
//方法:将得到的中缀表达式对应的List => 后缀表达式对应的List
public static List<String> parseSuffixExceptionList(List<String> ls){
Stack<String> stack = new Stack<>();// 符号栈
//说明:因为s2 这个栈,在整个转换过程中,没有pop操作,而且后面我们还需要逆序输出
//因此比较麻烦,这里我们就不用 Stack<String> 直接使用 List<String> s2
//Stack<String> s2 = new Stack<String>(); // 储存中间结果的栈s2
List<String> list = new ArrayList<>(); //临时存放数据
//遍历ls
for(String s:ls){
//如果是一个数,加入list
if(s.matches("\\d+")){
list.add(s);
}else if(s.equals("(")){
//入栈
stack.push(s);
}else if(s.equals(")")){
//如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
while(!stack.peek().equals("(")){//直至栈顶元素为"("结束
list.add(stack.pop());
}
stack.pop();//!!! 将 ( 弹出 s1栈, 消除小括号
}else{//为运算符
//当item的优先级小于等于s1栈顶运算符, 将s1栈顶的运算符弹出并加入到s2中,再次转到(4.1)与s1中新的栈顶运算符相比较
//问题:我们缺少一个比较优先级高低的方法
while(stack.size() != 0 && Operation.getValue(stack.peek()) >= Operation.getValue(s)){ //栈顶符号的优先级比s高
list.add(stack.pop());
}
stack.push(s);
}
}
//栈中剩余的操作符出栈存入list
while(stack.size() != 0){
list.add(stack.pop());
}
return list;
}
}
class Operation{
public static final int ADD=1;
public static final int SUB=1;
public static final int MUL=2;
public static final int DIV=2;
public static int getValue(String operation){
int res = 0;
switch (operation){
case "+":
res= ADD;
break;
case "-":
res= SUB;
break;
case "*":
res= MUL;
break;
case "/":
res= DIV;
break;
default:
System.out.println("输入操作符不存在!");
break;
}
return res;
}
}
后缀表达式运算
- 从左至右扫描,将数字入栈;
- 遇到运算符号,从栈种pop出两个数字,进行运算,运算结果再入栈;
- 数栈的最后一个结果就是最终的运算结果。