Leetcode-224 基本计算器

题目:

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

注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval()

 

示例 1:

输入:s = "1 + 1"
输出:2

示例 2:

输入:s = " 2-1 + 2 "
输出:3

示例 3:

输入:s = "(1+(4+5+2)-3)+(6+8)"
输出:23

 

提示:

  • 1 <= s.length <= 3 * 105
  • s 由数字、'+''-''('')'、和 ' ' 组成
  • s 表示一个有效的表达式
  • '+' 不能用作一元运算(例如, "+1" 和 "+(2 + 3)" 无效)
  • '-' 可以用作一元运算(即 "-1" 和 "-(2 + 3)" 是有效的)
  • 输入中不存在两个连续的操作符
  • 每个数字和运行的计算将适合于一个有符号的 32位 整数

思路: 双栈法

使用双栈法,维护一个操作数栈和一个操作符栈,操作符栈可以放左括号,并且遇到右括号时先弹栈计算,直到弹出左括号。第一个操作符不计算,随后遇到一个操作符先将之前的操作符计算完后再压栈,并将计算结果压进操作数栈。最后再处理栈中剩余的元素,处理完成后操作数栈存放的就是计算结果。

注意:

  • 设计一个计算函数,操作符和操作数弹栈操作都在此函数中进行。在弹栈过程中要注意判断栈中元素是否满足计算条件。压栈操作也在此函数中进行。
  • 在进行计算时只需判断操作符栈的情况就行。
  • 在遇到数字字符时,要把后面跟着的连续数字拼接起来作为完整的数字压栈。 - 新操作符入栈,先计算目前操作符栈内可计算的元素,要注意计算应当截至到遇到左括号或栈为空,因为只能先计算括号里面的元素。
  • 初始时,要在操作数栈放一个0,防止第一个操作数是负数的情况。
  • Java的字符串replaceAll函数返回一个新的改变后的字符串,需要赋值给原字符串才能完成原字符串的替换。
  • 为防止 () 内出现的首个字符为运算符,将所有的空格去掉,并将 (- 替换为 (0-,(+ 替换为 (0+(当然也可以不进行这样的预处理,将这个处理逻辑放到循环里去做)
class Solution {
    //使用双栈法,维护一个操作数栈和一个操作符栈,操作符栈可以放左括号,并且遇到右括号时先弹栈计算,直到弹出左括号。第一个操作符不计算,随后遇到一个操作符先将之前的操作符计算完后再压栈。最后再处理栈中剩余的元素。
    public int calculate(String s) {
        Stack<Integer> numStack = new Stack<>();
        Stack<Character> opStack = new Stack<>();

        //要在操作数栈中放一个0,避免出现第一个数字是负数的情况
        numStack.push(0);

        //字符串预处理空格,一元运算符,要注意给原字符串赋值
        s = s.replaceAll(" ", "");
        s = s.replaceAll("[(]-", "(0-");

        //将字符串转换为字符数组
        int n = s.length();
        char[] cs = s.toCharArray();

        for(int i = 0; i < n; ++i){
            //如果是数字,则压入操作数栈,由于可能存在多位数字,因此需要遇到第一位数字时也把后面的数字也读入,作为完整的数字。
            if(isNum(cs[i])){
                String numString = "";
                numString += cs[i];
                ++i;
                while(i < n && isNum(cs[i])){
                    numString += cs[i];
                    ++i;
                }
                int num = Integer.parseInt(numString);
                numStack.push(num);
                i--;
            }
            else if(cs[i] == '('){
                opStack.push(cs[i]);
            }
            else if(cs[i] == '+' || cs[i] == '-'){
                //新操作符入栈,先将栈内可计算的操作符计算了,直到遇到'('或操作符栈空,因为只能先计算括号里面的。
                while(!opStack.isEmpty() && opStack.peek() != '('){
                    calculateCurrentNum(numStack, opStack);
                }
                opStack.push(cs[i]);
            }
            else if(cs[i] == ')'){
                //要计算时只需要判断opStack栈就行了,毕竟有操作符才能计算
                while(!opStack.isEmpty()){
                    if(opStack.peek() != '('){
                        calculateCurrentNum(numStack, opStack);
                    }
                    else{
                        opStack.pop();
                        break;
                    }
                }
            }
        }
        //最后要把剩下的操作符计算完
        while(!opStack.isEmpty()){
            calculateCurrentNum(numStack, opStack);
        }
        //计算完成后,numStack栈存放了计算结果
        return numStack.peek();
    }

    private boolean isNum(char numChar){
        return Character.isDigit(numChar);
    }

    //计算和结果压栈都在这个函数里进行,无需返回值
    private void calculateCurrentNum(Stack<Integer> numStack, Stack<Character> opStack){
        if(numStack.size() < 2 || numStack.isEmpty())
            return;
        if(opStack.isEmpty())
            return;

        int rightNum = numStack.pop();
        int leftNum = numStack.pop();
        char op = opStack.pop();

        numStack.push((op == '+') ? leftNum+rightNum : leftNum-rightNum);
    }
}

学到和回忆了:

  • Java字符串相关用法
    public String replaceAll(String regex, String replacement):替换字符串内的某个字符串,应当注意Java中的String类是不可变的,因此要把返回的新字符串赋值给原字符串才能做到替换的效果
    public char[] toCharArray():将字符串转换为字符数组
    public char charAt(int index):获取指定索引处的字符
    public int length():获取字符串长度
    public boolean isEmpty():判断字符串是否为空
    public String concat(String s):将字符串s拼接在当前字符串后面
    public int indexOf(int ch ):获取指定字符在字符串中出现的位置
    public String substring(int beginIndex):获取当前字符串的子字符串
    public String trim():删除字符串的头尾空白符
    public boolean contains(CharSequence chars):判断当前字符串是否包含指定的字符或字符串
    public String toLowerCase():将字符串转换为小写
  • Java将字符串转为数字
    static int parseInt(String s)
  • Java判断字符是否为数字
    public static boolean isDigit(char ch)

参考:

posted @ 2022-04-14 21:30  心空之上  阅读(13)  评论(0)    收藏  举报