栈模拟计算器
计算器是我们生活中最常见的数学用具,计算器可以用栈来模拟。
模拟计算器要点:
- 使用两个栈分别来存储数字和运算符以及括号。
- 遍历表达式字符串,分别处理’(’,’)’,运算符和数字。 如果是 ‘(’ 则直接入栈, 如果是 ‘)’ 则计算括号中的结果,如果是运算符则根据前一个操作符判断要计算还是直接入栈,如果是数字则根据表达式后一位判断要入栈还是继续读取数字拼接。
- 遍历完字符串后,清空运算符栈,计算最终结果。
下面代码演示一下:
import java.util.Stack;
import java.util.Scanner;
public class Calculator {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String expression = sc.next(); // 表达式
if (expression.equals("end")) // 输入 end 结束
break;
System.out.printf("%s = %d\n", expression, count(expression));
}
}
// 计算表达式
public static int count(String expression) {
Stack<Integer> numStack = new Stack(); // 存放数字的栈
Stack<Character> operStack = new Stack(); // 存放运算符的栈
int num1 = 0, num2 = 0, res = 0;
char ch = ' ', oper = ' '; // ch是表达式中的每一字符,oper是运算符
String number = ""; // 用于拼接多位数
// 遍历字符串
for (int i = 0; i < expression.length(); i++) {
ch = expression.charAt(i);
if (ch == '(') { // 如果是左括号直接入栈即可
operStack.push(ch);
} else if (ch == ')') { // 如果是有括号,处理括号里面的所有内容
while (true) {
// 如果符号栈为'('
if (operStack.peek() == '(') {
operStack.pop();
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = calculate(num1, oper, num2);
numStack.push(res); // 把运算结果入栈
}
} else if (isOper(ch)) { // 如果是运算符
// 如果运算符栈为空时,该操作符直接入栈
if (operStack.isEmpty() || operStack.peek() == '(') { // 利用惰性
operStack.push(ch);
} else { // 如果运算符栈不为空或'('时,则需要判断要不要计算
// 如果当前运算符ch优先级小于或等于前一个运算符,需先计算前一个运算符
if (priority(ch) <= priority(operStack.peek())) {
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = calculate(num1, oper, num2);
numStack.push(res); // 把运算结果入栈
operStack.push(ch); // 把当前运算符压入栈
} else { // 如果当前运算符ch优先级大于前一个运算符,直接入栈
operStack.push(ch);
}
}
} else { // 如果是数字直接入栈
number += ch;
// 如果已经是最后一位了
if (i == expression.length() -1) {
numStack.push(Integer.parseInt(number));
} else if(isOper(expression.charAt(i+1)) || expression.charAt(i+1) == '(' || expression.charAt(i+1) == ')') {
numStack.push(Integer.parseInt(number));
number = "";
}
}
}
// 表达式遍历结束,清理栈
while (true) {
// 如果符号栈为空,退出
if (operStack.isEmpty()) {
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = calculate(num1, oper, num2);
numStack.push(res); // 把运算结果入栈
}
// 此时数字栈中只剩下一个数字,也就是最终的计算结果,返回即可
return numStack.pop();
}
// 判断优先级
public static int priority(char oper) {
if (oper == '*' || oper == '/')
return 1;
else if (oper == '+' || oper == '-')
return 0;
else
return -1;
}
// 判断是否是运算符
public static boolean isOper(char val) {
return val == '+' || val == '-' || val == '*' || val == '/';
}
// 返回计算结果
public static int calculate(int num1, char oper, int num2) {
int res = 0;
switch (oper) {
case '+':
res += num2 + num1;break;
case '-':
res += num2 - num1;break;
case '*':
res += num2 * num1;break;
case '/':
res += num2 / num1;break;
default:
break;
}
return res;
}
}
测试一下,这里用两组数据,12*(2-5+1)/2+24,2+(22*6-10)/2 测试,以end结束,运行结果为:
12*(2-5+1)/2+24
12*(2-5+1)/2+24 = 12
2+(22*6-10)/2
2+(22*6-10)/2 = 63
end
上述是对中缀表达式的计算,想了解波兰表达式和逆波兰表达式可看
波兰计算器和逆波兰计算器