LeetCode-227. 基本计算器 II

题目来源

227. 基本计算器 II

题目详情

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

整数除法仅保留整数部分。

示例 1:

输入: s = "3+2*2"
输出: 7

示例 2:

输入: s = " 3/2 "
输出: 1

示例 3:

输入: s = " 3+5 / 2 "
输出: 5

提示:

  • 1 <= s.length <= 3 * 105
  • s 由整数和算符 ('+', '-', '*', '/') 组成,中间由一些空格隔开
  • s 表示一个 有效表达式
  • 表达式中的所有整数都是非负整数,且在范围 [0, 231 - 1]
  • 题目数据保证答案是一个 32-bit 整数

题解分析

解法一:单栈

  1. 本题与传统的算术表达书相比更简单一点,因为这里并没有左右括号,所以可以不必使用两个栈。
  2. 这里只需要使用一个栈来存储计算结果,当遇到运算符时,可以根据一个前置运算符计算出前面一部分的运算结果,即栈顶与当前元素的运算结果,并将结果重新入栈。
  3. 本题可以先遍历整个字符串,并根据连续的数字字符累加出一个完整的数字,接着根据遇到的当前字符来重新计算栈顶元素。
class Solution {
    public int calculate(String s) {
        Deque<Integer> sta = new LinkedList<>();
        int n = s.length();
        int num = 0;
        char preSign = '+';// 前置运算符
        for(int i=0; i<n; i++){
            char ch = s.charAt(i);
            if(Character.isDigit(ch)){
                num = num * 10 + ch - '0';
            }
            // 遇到最后一个字符或者遇到运算符,则计算栈顶
            if(i == n-1 || (!Character.isDigit(ch) && ch != ' ')){
                switch(preSign){
                    case '+':
                        sta.push(num);
                        break;
                    case '-':
                        sta.push(-num);
                        break;
                    case '*':
                        sta.push(sta.pollFirst() * num);
                        break;
                    default:
                        sta.push(sta.pollFirst() / num);
                }
                num = 0;
                preSign = ch;
            }
        }
        int res = 0;
        while(!sta.isEmpty()){
            res += sta.pollFirst();
        }
        return res;
    }
}

解法二:双栈法

  1. 类似于逆波兰表达式的求解思路,这里可以使用双栈来帮助解决问题。其中,操作数栈用来存储运算符号,而数值栈用来存储中间计算过程。
  2. 需要注意的是,我们需要先对字符串进行预处理,也就是删除字符串中原有的空字符,以免循环过程中对这个特殊字符进行额外的处理。
  3. 此外,为了防止第一个出现的是负数,需要先在数值栈中放置一个0。
  4. 另外,考虑到有括号的情况,可能出现(-的形式,所以需要对这种情况进行一步额外的处理,即在数值栈中增加一个0,使之转换为(0-的合法形式。
class Solution {
    // 操作符优先级
    Map<Character, Integer> map= new HashMap<>(){{
        put('+', 1);
        put('-', 1);
        put('*', 2);
        put('/', 2);
        put('%', 2);
    }};
    public int calculate(String s) {
        Deque<Integer> numsta = new LinkedList<>();// 数值栈
        Deque<Character> opsta = new LinkedList<>();// 操作数栈
        s = s.replaceAll(" ", "");// 删除所有空格
        int n = s.length();
        numsta.offerFirst(0);
        for(int i=0; i<n; i++){
            char ch = s.charAt(i);
            if(ch == '('){
                opsta.offerFirst(ch);
            }else if(ch == ')'){// 遇到右括号则一直进行计算,直到遇到左括号
                while(!opsta.isEmpty()){
                    if(opsta.peekFirst() != '('){
                        cal(numsta, opsta);
                    }else{
                        opsta.pollFirst();// 出栈左括号
                        break;
                    }
                }
            }else if(Character.isDigit(ch)){
                int num = 0;
                for(; i<n && Character.isDigit(s.charAt(i)); i++){
                    num = num * 10 + s.charAt(i) - '0';
                }
                i--;
                numsta.offerFirst(num);
            }else{// 遇到操作符,则计算栈中优先级高于当前运算符之间的元素
                if(i > 0 && s.charAt(i-1) == '(' && (ch == '+' || ch == '-')){
                    numsta.offerFirst(0);
                }
                while(!opsta.isEmpty() && opsta.peekFirst() != '('){
                    char preops = opsta.peekFirst();
                    System.out.println(preops + ", " + ch);
                    if(map.getOrDefault(preops, 0) >= map.getOrDefault(ch, 0)){
                        cal(numsta, opsta);
                    }else{
                        break;
                    }
                }
                opsta.offerFirst(ch);
            }
        }
        while(!opsta.isEmpty()){
            cal(numsta, opsta);
        }
        return numsta.peekFirst();
    }

    private void cal(Deque<Integer> numsta, Deque<Character> opsta){
        if(numsta.isEmpty() || numsta.size() < 2){
            return;
        }
        if(opsta.isEmpty()){
            return;
        }
        char ops = opsta.pollFirst();
        int num2 = numsta.pollFirst();
        int num1 = numsta.pollFirst();
        int res = 0;
        switch(ops){
            case '+':
                res = num1 + num2;
                break;
            case '-':
                res = num1 - num2;
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                res = num1 / num2;
                break;
            case '%':
                res = num1 % num2;
                break;
            default:
                res = 0;
        }
        numsta.offerFirst(res);
    }
}

参考

【宫水三叶】使用「双栈」解决「究极表达式计算」问题

结果展示

image-20220131115516006

posted @ 2022-01-31 19:41  Garrett_Wale  阅读(150)  评论(0编辑  收藏  举报