示例 1:
输入:s = “3+2*2”
输出:7
示例 2:
输入:s = " 3/2 "
输出:1
示例 3:
输入:s = " 3+5 / 2 "
输出:5
思路:
- 使用两个栈,其中一个push数字,另一个push运算符
- 由于四则运算中乘除的优先级高于加减,遍历扫描至加号和减号push入栈,遇到乘号或除号时pop出数字栈的顶部和运算符的后一个数字先做运算,将运算结果push入数字栈
- 遍历完毕后数字栈中都是数字,运算符栈只有加减号
package stack_queue;
import java.util.Stack;
public class Num227_Calculate {
//判断是数字还是运算符的方法
public static boolean isNum(String s){
try{
Integer.parseInt(s);
return true;
}
catch(NumberFormatException e){
return false;
}
}
public static int calculate(String s){
String[] str = s.split(" ");
Stack<Integer> st1 = new Stack<>(); //放数字
Stack<String> st2 = new Stack<>();//放符号
for (int i = 0; i < str.length; i++){
int sum = 0;
if (isNum(str[i])){ //碰到的是数字
st1.push(Integer.parseInt(str[i]));
}
else{ //碰到的是运算符
if (str[i].equals("+") || str[i].equals("-")){
st2.push(str[i]);
}
else{ //乘除的话要弹出一前一后两个数字
int num1 = Integer.parseInt(str[i+1]);//后面的那个
int num2 = st1.pop();//前面的那个
if (str[i].equals("*")){
sum = num1 * num2;
}
else{
sum = num2 / num1;
}
i++; //跨过后面那个数字
st1.push(sum);
}
}
}
//遍历完之后st1只有数字,st2只有加号或减号
while (!st2.isEmpty()){
int num1 = st1.pop();
int num2 = st1.pop();
int sum = 0;
if (st2.pop().equals("+")){
sum = num1 + num2;
}
else{
sum = num2 - num1;
}
st1.push(sum);
}
return st1.peek();
}
public static void main(String[] args) {
String s = "3 * 5 + 2";
System.out.println(calculate(s));
}
}
但是这么写是有问题的,使用了split用空格分割将String转为字符串数组,要求测试用例每个字符都用空格隔开才能行得通,但是实际上测试用例的空格插入似乎是随机的
以下是官方题解,只使用了一个栈,引入了变量num来担当运算符后一个数字的对象:
class Solution {
public int calculate(String s) {
Deque<Integer> stack = new ArrayDeque<Integer>();
char preSign = '+';
int num = 0;
int n = s.length();
for (int i = 0; i < n; ++i) {
if (Character.isDigit(s.charAt(i))) {
num = num * 10 + s.charAt(i) - '0';
} //*10是因为可能位数不止有一个
if (!Character.isDigit(s.charAt(i)) && s.charAt(i) != ' ' || i == n - 1) {
switch (preSign) {
case '+':
stack.push(num);
break;
case '-':
stack.push(-num);
break;
case '*':
stack.push(stack.pop() * num);
break;
default:
stack.push(stack.pop() / num);
}
preSign = s.charAt(i);
num = 0;
}
}
int ans = 0;
while (!stack.isEmpty()) {
ans += stack.pop();
}
return ans;
}
}
不过我比较笨,想不到官方的题解,还是用双栈,第一版修改后的代码唯一的问题就是参与运算的数字如果超过各位数就会出错
import java.util.Stack;
public class Num227_Calculate {
//判断是数字还是运算符的方法
public static boolean isNum(char c){
if (c >= '0' && c <= '9'){
return true;
}
return false;
}
public static int calculate(String s){
s = s.replace(" ","");
char[] arr = s.toCharArray();
Stack<Integer> st1 = new Stack<>(); //放数字
Stack<Character> st2 = new Stack<>();//放符号
for (int i = 0; i < arr.length; i++){
if (isNum(arr[i])){
st1.push(Integer.parseInt(String.valueOf(arr[i])));
}
else{
if (arr[i] == '+' || arr[i] == '-'){
st2.push(arr[i]);
}
else{
int sum = 0;
int num1 = Integer.parseInt(String.valueOf(arr[i+1]));
int num2 = st1.pop();
if (arr[i] == '*'){
sum = num1 * num2;
}
else{
sum = num2 / num1;
}
st1.push(sum);
i++;
}
}
}
//遍历完之后st1只有数字,st2只有加号或减号
while (!st2.isEmpty()){
int num1 = st1.pop();
int num2 = st1.pop();
int sum = 0;
if (st2.pop() == '+'){
sum = num1 + num2;
}
else{
sum = num2 - num1;
}
st1.push(sum);
}
return st1.peek();
}
public static void main(String[] args) {
String s = "30 * 5+2";
System.out.println(calculate(s));
}
}
但是后来发现前后两个数字都有可能超过各位数,一定要有变量来存储前一个数和前一个运算符,那就是官方题解了
|| i == n - 1的目的是:因为字符串的末尾一定是数字,进入是数字的分支后刷新num,如果没有该语句的话最后一个运算会被略过,刷新的num不会被使用
举个例子:3 * 2
i = 0,num = 3,preSign = +,栈为空
i = 1,num = 0,preSign = *,栈为3
i = 2,num = 2,preSign = *,栈为3
并没有执行运算