逆波兰计算器
- 需求:
Java、支持四则运算、小括号、多位数 - 结果
- 函数
主函数、中缀表达式字符串转list集合、中缀表达式list集合转后缀表达式list集合、运算、优先级比较 - 实现类
主函数实现
public static void main(String[] args) {
/**
* 中缀表达式转后缀表达式
* 目的:1+((2+3)*4)-5 => 1 2 3 + 4 * + 5 -
* 思路:直接对str操作不方便,先将“1+((2+3)*4)-5” 转化为对应的List
* 1+((2+3)*4)-5 => ArrayList[1 ,2, 3, +, 4, *, +, 5, -]
*/
String expression = "1+((2+3)*4)-5";
List<String> infixExpressionList = toInfixExpressionList(expression); // 中缀表达式 字符串 => 对应的list集合
System.out.println("中缀表达式对应的List=" + infixExpressionList); // ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
List<String> suffixExpresionList = parseSuffixExpresionList(infixExpressionList); // 中缀表达式list集合 => 后缀表达式list集合
System.out.println("后缀表达式对应的List" + suffixExpresionList); // ArrayList [1,2,3,+,4,*,+,5,-]
System.out.printf("expression=%d", calculate(suffixExpresionList)); // 计算结果
}
中缀表达式字符串转list集合
/**
* 中缀表达式 字符串 转成 对应的List集合
* 注意点(关于数组越界):
* 目标表达式:"1+((2+3)*4)-5" length = 13 下标是 0 ~ 12
* while(i < s.length() && (c=s.charAt(i)) >= 48 && (c=s.charAt(i)) <= 57) {
* str += c;
* i++;
* }
* 当扫描到5时,i = 12,接着执行 i++ , i = 13 ,接着返回while条件处判断是否跳出while循环
* 如果这时while条件判断中不加 i < s.length(),那么s.charAt(13)就会报错:数组越界
* @param s
* @return
*/
public static List<String> toInfixExpressionList(String s) {
// 定义List
List<String> ls = new ArrayList<String>();
int i = 0; // 指针 遍历中缀表达式字符串
String str; // 多位数拼接
char c; // 遍历到的字符放入c
do { // 1+((2+3)*4)-5
// 当c非数字,加入到ls
if((c=s.charAt(i)) < 48 || (c=s.charAt(i)) > 57) { // charAt 返回指定索引处的字符 范围[0,length-1]
ls.add("" + c);
i++; // i 后移
} else { //如果是一个数,需要考虑多位数
str = ""; // str 置 ""
while(i < s.length() && (c=s.charAt(i)) >= 48 && (c=s.charAt(i)) <= 57) { // '0'[48]->'9'[57]
str += c; // 拼接
i++;
}
ls.add(str);
}
}while(i < s.length());
return ls;
}
中缀表达式list集合转后缀表达式list集合
/**
* 中缀List转后缀List
* ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] ==> ArrayList [1,2,3,+,4,*,+,5,-]
* 注意:使用系统栈时,在整个转换过程中,没有pop操作,而且之后还需逆序输出
* 由此一来,十分麻烦,于是我们采用List<String> 代替 Stack<String>
* Stack<String> s2 = new Stack<String>();
* 学习点:
* stack.peek() 返回栈顶元素,但不在堆栈中删除它。
* Stack.pop() 返回栈顶元素,并在进程中删除它。
* @param ls :中缀list
* @return
*/
public static List<String> parseSuffixExpresionList(List<String> ls) {
// 定义栈
Stack<String> opera = new Stack<String>(); // 符号栈
List<String> num = new ArrayList<String>(); // 中间结果值
// 遍历表达式
for(String item: ls) {
// 数入num栈
if(item.matches("\\d+")) {
num.add(item);
} else if (item.equals("(")) {
opera.push(item);
} else if (item.equals(")")) {
// 遇到右括号,依次弹出opera栈栈顶的运算符,并压入num栈,直到遇到左括号停止,此时丢弃这一对括号
while(!opera.peek().equals("(")) {
num.add(opera.pop());
}
opera.pop(); // !!! 弹出opera栈中左括号,消除
} else {
// 当item的优先级小于等于opera栈顶运算符,将opera栈顶运算符弹出并加入到num栈中,再次转到(4.1)与opera中新的栈顶运算符相比较
while(opera.size() != 0 && Operation.getValue(opera.peek()) >= Operation.getValue(item) ) {
num.add(opera.pop());
}
// item压入栈
opera.push(item);
}
}
// 将opera剩余的运算符依次弹出加入num栈
while(opera.size() != 0) {
num.add(opera.pop());
}
// 注意:由于存放到List , 因此按顺序输出就是对应的后缀表达式对应的List
return num;
}
运算
/**
* 完成逆波兰表达式的运算
* @param ls
* @return
*/
public static int calculate(List<String> ls) {
// 定义栈 只需一个栈即可
Stack<String> stack = new Stack<String>();
// 遍历ls
for (String item : ls) {
// 正则表达式取出数
if (item.matches("\\d+")) { // 匹配多位数
// 入栈
stack.push(item);
} else {
// pop出两个数,运算,再入栈
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if (item.equals("+")) {
res = num1 + num2;
} else if (item.equals("-")) {
res = num1 - num2;
} else if (item.equals("*")) {
res = num1 * num2;
} else if (item.equals("/")) {
res = num1 / num2;
} else {
throw new RuntimeException("运算符有误");
}
// res入栈
stack.push("" + res);
}
}
// 最后留在stack中的数据是运算结果
return Integer.parseInt(stack.pop());
}
}
优先级比较
/**
* 返回一个运算符对应的优先级
*/
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 operation) {
int result = 0;
switch (operation) {
case "+":
result = ADD;
break;
case "-":
result = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
System.out.println("不存在该运算符" + operation);
break;
}
return result;
}