150. 逆波兰表达式求值(中等)
给你一个字符串数组
tokens,表示一个根据 逆波兰表示法 表示的算术表达式。请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
 '+'、'-'、'*'和'/'。- 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
 - 两个整数之间的除法总是 向零截断 。
 - 表达式中不含除零运算。
 - 输入是一个根据逆波兰表示法表示的算术表达式。
 - 答案及所有中间计算结果可以用 32 位 整数表示。
 
备注:向零截断是一种取整方式,也称为截断取整。这种取整方法将一个浮点数取整为最接近但小于它的整数。具体来说,对于正数,向零截断会取其整数部分;对于负数,向零截断会取其绝对值的整数部分并添加负号。例如,17 / 10 = 1,5 / 2 = 2,而 -9 / 4 = -2。
解法一、双向队列模拟栈
双向队列相关写在碎碎念里了,然后发现不用default,可以用isNumber判别
class Solution {
    public static int evalRPN(String[] tokens) {
        Deque<Integer> num = new ArrayDeque<>();
        for(String s : tokens){
            int len = num.size();
            int a,b;
            switch (s){
                case "+":
                    a = num.removeLast();//第一个
                    b = num.removeLast();//第二个
                    num.add(a+b);
                    break;
                case "-":
                    a = num.removeLast();//第一个
                    b = num.removeLast();//第二个
                    num.add(b-a);
                    break;
                case"/":
                    a = num.removeLast();//第一个
                    b = num.removeLast();//第二个
                    num.add(b/a);
                    break;
                case"*":
                    a = num.removeLast();//第一个
                    b = num.removeLast();//第二个
                    num.add(b*a);
                    break;
                default:
                    int t = Integer.parseInt(s);
                    num.add(t);
                    break;
            }
        }
        return num.pop();
    }
} 

227. 基本计算器 II(中等)
给你一个字符串表达式
s,请你实现一个基本计算器来计算并返回它的值。整数除法仅保留整数部分。
你可以假设给定的表达式总是有效的。所有中间结果将在
[-231, 231 - 1]的范围内。注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如
eval()。
解法一、栈
这里不用if else结构是因为需要判断那个i==n-1,如果最后一个是数字,它依旧要考虑操作符号。
大体:对于第一个数字,默认的op是‘+’,对于其他的,可以用preSign记录下来。如果是数字,则不断循环,处理num值。如果不是数字或者是最后一个数字,则考虑操作符。这里不需要考虑空格,把空格跳掉即可,空格只是干扰项。
说起来这个A&&B||C的结构也很神奇,等效于(A&&B)||C,原来&和|是有优先级区分的(没细想过这个问题)
class Solution {
    public static 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';
            }
            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;
    }
} 
 
解法二、模拟
评论区看来的~↓
用last巧妙地记录和模拟了栈顶结构。
. - 力扣(LeetCode)
class Solution {
public:
    int calculate(string s) {
        int ret = 0, last = 0;
        int value = 0;
        char op = '+';
        s += "+";
        for(auto &c:s) {
            if(isdigit(c))
                value = value * 10 + (c - '0');
            else if(c != ' ') {
                if(op == '+') {
                    ret += value;
                    last = value;
                }else if(op == '-') {
                    ret -= value;
                    last = -value;
                }else if(op == '*') {
                    ret = ret - last + value * last;
                    last = value * last;
                }else {
                    ret = ret - last + last/value;
                    last = last/value; 
                }
                op = c;
                value = 0;
            }
        }
        return ret;
    }
}; 
 
224. 基本计算器(困难)
给你一个字符串表达式
s,请你实现一个基本计算器来计算并返回它的值。注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如
eval()。示例 1:
输入:s = "1 + 1" 输出:2示例 2:
输入:s = " 2-1 + 2 " 输出:3示例 3:
输入:s = "(1+(4+5+2)-3)+(6+8)" 输出:23
解法一、栈+模拟
num存储数字,preOp参考227,i因为while是i++判断所以从-1开始,ops记录层数,默认一层为1,遇到(则push,遇到)则pop。因为只有加减,所以用1、-1即可
三个if(其中一个是if-else)的顺序很重要,第一个决定数字,第二个决定加减,第三个决定括号栈
易错点:①res的处理②preOp的设置③括号时ops的设置④左括号时把preOp置1
class Solution {
    public static int calculate(String s) {
        Deque<Integer> ops = new ArrayDeque<>();
        int res = 0, num = 0, preOp = 1;
        int i = -1, len = s.length();
        ops.push(1);
        //单指针循环遍历
        while (++i < len) {
            if (Character.isDigit(s.charAt(i))) {
                num = num * 10 - '0' + s.charAt(i);
            }
            if (s.charAt(i) == '+' || s.charAt(i) == '-' || s.charAt(i) == ')'|| i == len-1 ) {
                res += num * preOp * ops.element();
                if (s.charAt(i) == '+') preOp = 1;
                else preOp = -1;
                num = 0;
            }
            if (s.charAt(i) == '(') {;
                ops.push(ops.element() * preOp);
                preOp = 1;
            } else if (s.charAt(i) == ')') {
                ops.pop();
            }
        }
        return res;
    }
} 
 
解法二、改进版
空格跳过,处理数字,处理符号,清晰易懂。无论哪种做法,都要记得一开始push一个1的默认值,当作全部表达式最外层套了个()处理
class Solution {
public:
    int calculate(string s) {
        stack<char> st; // 存储正负号
        int ans = 0, num = 0, op = 1;
        st.push(op);
        for (char c : s) {
            if (c == ' ') continue;
            else if (c >= '0') num = num * 10 - '0' + c;
            else {
                ans += op * num;
                num = 0;
                if (c == '+') op = st.top();
                else if (c == '-') op = -st.top();
                else if (c == '(') st.push(op); // 将括号前符号放入栈顶
                else st.pop();
            }
        }
        return ans + op * num;
    }
}; 
碎碎念:
- 理解了这里用到的双向队列是ArrayDeque,头是0,尾/最后是末端(序号较大处)。push是从头(0)开始加,add是从尾开始加,和stack是有差别的。稍微地熟悉了element/peek之类的差别
 - 150简单使用,227稍微有些复杂(中途会错意了),224注意一下脉络和思路再写会好很多,这个需要复刷
 



















