HDU1237——简单计算器
时间限制:2000ms;空间限制:65536KB。
问题描述:读人一个只包含+、-、*、/ 的非负整数运算的表达式,计算该表达式的值。
输入格式:测试输人包含若千测试用例,每个测试用例占一行,每行不超过 200个字符,整数和运算符之间用一个空格分隔;没有非法表达式;当一行中只有 0 时输人结束,相应的结果不要输出。
输出格式:对每个测试用例输出一行,即该表达式的值,精确到小数点后两位。
输人样例:
1 + 2
4 + 2 * 5 - 7 / 11
0
输出样例:
3.00
13.36
思路一
- 定义两个栈,一个用来存数字,一个用来存运算符号。
- 将输入的测试用例分割成一个 String 类型的数组,用 for 循环进行遍历
- 如果是数字,就直接入数字栈,如果运算符号,如果符号栈为空,就入栈,如果栈不为空就需要判断要入栈的符号的优先级是否大于栈顶符号的优先级,如果大于的话就入栈,如果小于或者等于的话,就从符号栈取出符号,再从数字栈中取出两个数字进行计算,再把计算的结果放入数字栈中,直到要入栈的符号的优先级大于栈顶符号的优先级,然后把需要入栈的符号进行入栈。
- 而对于符号的优先级的话,"+"、"-" 的优先级一样,属于低级," * "、" / " 的优先级一样,属于高级
- 最后需要判断一下符号栈中是否还有元素,如果还存在元素的话,需要从数字栈中取出两个数字进行计算,然后把计算的结果存入数字栈中,直到符号栈中没有元素
- 此时,数字栈的元素就是最后的结果,只需把数字栈中的元素取出来就可以了
代码
import java.util.Scanner;
import java.util.Stack;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true) {
String s1 = sc.nextLine();
if (s1.equals("0")) { //输入为 0 表示结束,就退出循环
break;
}
String[] s = s1.split(" "); //将测试用例用空格分割开来
Stack num = new Stack<Double>(); //数字栈
Stack symbol = new Stack(); //运算符栈
for (int i = 0; i < s.length; i++) {
if (s[i].equals("+") || s[i].equals("-") || s[i].equals("*") || s[i].equals("/")) { //如果是运算符号
if (symbol.isEmpty()) { //如果是运算符号且栈为空
symbol.push(s[i]);
} else { //如果是运算符号且栈不为空
if (priority(s[i]) > priority((String) symbol.peek())) { //如果要入栈的符号的优先级大于栈顶符号的优先级
symbol.push(s[i]); //就让它入栈
} else { //要入栈的符号的优先级小于栈顶符号的优先级
while (!symbol.isEmpty() && priority(s[i]) <= priority((String) symbol.peek())) {
String s2 = (String) symbol.pop(); //取出符号栈中栈顶的元素
double res = cal((double) num.pop(), (double) num.pop(), s2); //然后进行计算
num.push(res); //把计算的结果放入数字栈中
}
symbol.push(s[i]); //退出while循环,说明此时要入栈的元素的优先级大于栈顶的元素
//所以把要原本要入栈的运算符号进行入栈
}
}
} else {
num.push(Double.valueOf(s[i])); //数字,直接入数字栈
}
}
while (!symbol.isEmpty()) { //判断一下符号栈中是否还有元素
//进行计算,然后把计算的结果存入数字栈中
num.push(cal((Double) num.pop(), (Double) num.pop(), (String) symbol.pop()));
}
System.out.printf("%.2f\n", num.pop()); //取出数字栈的最后一个元素
}
}
public static int priority(String op) { //优先级方法
if (op.equals("*") || op.equals("/")) {
return 1;
} else if (op.equals("+") || op.equals("-")) {
return 0;
} else
return -1;
}
public static double cal(double num1, double num2, String op) { //计算方法
switch (op) {
case "+":
return num1 + num2;
case "-":
return num2 - num1; //因为栈顶的元素是被减数,所以是num2在前面
case "*":
return num1 * num2;
case "/":
return num2 / num1; 因为栈顶的元素是被除数,所以是num2在前面
}
return 0;
}
}
思路二
- 只需要定义一个数字栈就行,用来存放数字
- 每次都读一行,然后用 split 分割成 String 数组,数组中第一个元素一定是数字,所以先把数组中第一个元素先存入栈中,再循环遍历数组中的元素
- 如果遇到的运算符是"+"的话,就说明下一个元素一定是数字,就把它先存入栈中
- 如果遇到的运算符是"-"的话,就取下一个元素的相反数,然后存入栈中
- 如果遇到的运算符是" * "的话,就从栈顶取一个元素,与乘号的下一个元素相乘,再把结果存入栈中
- 如果遇到的运算符是" / "的话,就从栈顶取一个元素,取除号的下一个元素的倒数,让它们相乘,再把结果存入栈中
- 最后栈中可能还存在没有参与运算的数字,把栈中的数字全部相加,直到栈为空
思路二相对于思路一来说更加简单明了,更容易理解与读懂 ,所以思路二的代码就不写注释了
代码
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true) {
Stack<Double> stack = new Stack<Double>();
double sum = 0;
String s = sc.nextLine();
if (s.equals("0")) {
break;
}
String[] split = s.split(" ");
stack.push(Double.parseDouble(split[0]));
for (int i = 1; i < split.length; i++) {
if (split[i].equals("+")) {
stack.push(Double.parseDouble(split[++i]));
} else if (split[i].equals("-")) {
stack.push(-Double.parseDouble(split[++i]));
} else if (split[i].equals("*")) {
stack.push(stack.pop() * Double.parseDouble(split[++i]));
} else if (split[i].equals("/")) {
stack.push(stack.pop() * (1 / Double.parseDouble(split[++i])));
}
}
while (!stack.isEmpty()) {
sum += stack.pop();
}
System.out.printf("%.2f", sum);
}
}
}