[栈 字符串] 224. 基本计算器(栈的应用:符号栈+数字栈+出入栈规则、字符串解析) 227.基本计算器 II(出入栈规则、字符串解析)

224. 基本计算器(设计带括号的加减法)

题目链接:https://leetcode-cn.com/problems/basic-calculator/


分类:

  • 栈(开辟符号栈、数字栈,设计出入栈规则,实现带括号的加减法)
  • 数学/字符串(分析+实现基本计算器的功能,实质上是对字符串的解析:对数字、括号字符、加减法号的提取)

在这里插入图片描述

思路:栈 + 字符串解析

开辟两个栈sign和nums,sign用于存放运算符号,nums用于存放数字。

不考虑括号时,栈的工作流程如下:

  • 遇到运算符号时,就将符号压入sign栈中;

  • 遇到数字num,就将nums栈顶top弹出作为因子1,弹出sign栈的栈顶作为运算符,当前数字num作为因子2,计算因子1 +/- 因子2,将运算结果存入nums栈。

    这样就能实现基本的加减法。

考虑括号时,就在无括号的基础上增加一定的处理:

  • 遇到非括号的符号 或 左括号时,就将符号压入sign栈中;

  • 遇到右括号,就将sign栈顶的左括号弹出,left-1,

    接着做循环判断:如果此时sign栈顶为非括号符号,则弹出nums栈的两个栈顶,前一个栈顶做因子1,后一个栈顶做因子2,弹出sign的栈顶作为运算符,计算:因子1 +/- 因子2,运算结果存入nums栈。

    循环直到栈为空或遇到左括号为止

  • 遇到数字时,判断此时sign栈顶是什么符号:

    • 如果是左括号或为空,则数字直接入nums栈;
    • 如果是其他符号,则按之前的工作流程进行:就将nums栈顶top弹出作为因子1,弹出sign栈的栈顶作为运算符,当前数字num作为因子2,计算因子1 +/- 因子2,将运算结果存入nums栈。
实现遇到的问题:数字截取问题

数字不是只有个位数,所以遇到数字时要截取数字的左右边界,再转换成对应数字:

    //寻找数字的左右边界
     int start = i;
     while(i < chars.length && chars[i] >= '0' && chars[i] <= '9') i++;
     int num2 = Integer.parseInt(s.substring(start, i));

实现代码:

class Solution {
    public int calculate(String s) {
        Deque<Integer> nums = new ArrayDeque<>();//数字栈
        Deque<Character> signs = new ArrayDeque<>();//符号栈
        char[] chars = s.toCharArray();
        int i = 0;
        while(i < s.length()){
            //遇到空格符直接跳过
            if(chars[i] == ' '){
                i++;
                continue;
            }
            //遇到左括号、非括号运算符直接入符号栈
            else if(chars[i] == '(' || chars[i] == '+' || chars[i] == '-'){
                signs.push(chars[i]);
                i++;
            }
            //遇到右括号
            else if(chars[i] == ')'){
                signs.pop();//弹出栈顶的左括号
                while(!signs.isEmpty() && signs.peek() != '('){
                    int num2 = nums.pop();
                    int num1 = nums.pop();
                    char sign = signs.pop();
                    int res = helper(num1, num2, sign);
                    nums.push(res);
                }
                i++;
            }
            //遇到数字
            else{
                //寻找数字的左右边界
                int start = i;
                while(i < chars.length && chars[i] >= '0' && chars[i] <= '9') i++;
                int num2 = Integer.parseInt(s.substring(start, i));
                //符号栈为空或栈顶为左括号,数字直接入栈
                if(signs.isEmpty() || signs.peek() == '(') nums.push(num2);
                else{
                    int num1 = nums.pop();
                    char sign = signs.pop();
                    int res = helper(num1, num2, sign);
                    nums.push(res);
                }
            }
            
        }
        return nums.peek();
    }
    //执行计算的函数
    public int helper(int num1, int num2, char sign){
        switch(sign){
            case '+':
                return num1 + num2;
            case '-':
                return num1 - num2;
            default:
                return 0;
        }
    }
}

227. 基本计算器 II(设计无括号的加减乘除法)

题目链接:https://leetcode-cn.com/problems/basic-calculator-ii/


分类:

  • 栈(只用一个栈,设计出入栈规则,实现无括号的加减乘除法)
  • 数学/字符串(分析+实现基本计算器的功能,实质上是对字符串的解析:提取数字(难点)、加减乘除号)

在这里插入图片描述

题目分析

这题在224的基础上去掉了括号,但增加了乘除法,题目的考点在于:乘除法的优先级大于加减法要如何处理。

思路:栈 + 出入栈规则的妙用

  1. 和224一样仍然使用栈来实现,但不必分符号栈数字栈,只开辟一个栈,存放数字。

  2. 遍历字符串s:

  • 如果遇到第一个数字或数字前面有加减号,将数字连同加减号一并加入栈中;
  • 如果遇到的是 * 或 /,就取符号后的数字作为num2,弹出栈顶作为num1,计算num1 * 或/ num2,将计算结果存入栈中。
  1. 字符串s遍历结束后,将栈内所有元素相加就得到最终的结果。
实现时遇到的问题:数字提取问题

字符串内部可能存在空格符,所以在截取字符串上的一个数字时不能像224题那样使用substring + parseInt的方式,而要改为:遍历一个个字符+数值迭代更新的方法:

    //根据当前的起点索引i寻找一个完整的数字
    public int getNum(String s){
        int num = 0;
        for(; i < s.length(); i++){
            if(s.charAt(i) == ' ') continue;
            if(s.charAt(i) < '0' || s.charAt(i) > '9') break;
            num = num * 10 + s.charAt(i) - '0';
        }
        return num;
    }
  • 按上面的方法提取数字可以避免字符串内部空格符的影响。

为了确保在获取数字后,字符串能够继续从数字右边界的下一个字符处开始工作,我们把字符串的工作指针设置为全局变量,因此for-i循环寻找数字的过程中 i++ 影响的是整个遍历过程的工作指针。

实现代码:

class Solution {
    int  i = 0;//字符串全局工作指针
    public int calculate(String s) {
        Deque<Integer> stack = new ArrayDeque<>();
        while(i < s.length()){
            //遇到空格符直接跳过
            if(s.charAt(i) == ' ') i++;
            //如果遇到+-号,则将其后的数字连同符号一起入栈
            else if(s.charAt(i) == '+' || s.charAt(i) == '-'){
                int sign = s.charAt(i) == '+' ? 1 : -1;
                i++;
                //寻找符号后面的数字
                int num = getNum(s);
                stack.push(sign * num);
            }
            //如果遇到的是*或/号,则取栈顶数字和其后的数字做乘或除法,再将计算结果入栈
            else if(s.charAt(i) == '*' || s.charAt(i) == '/'){
                char sign = s.charAt(i);
                i++;
                //先寻找其后的数字
                //寻找符号后面的数字
                int num2 = getNum(s);
                int num1 = stack.poll();
                int res = 1;
                if(sign == '*') res = num1 * num2;
                else res = num1 / num2;
                stack.push(res);
            }
            //如果遇到的是数字,直接加入栈中
            else{
                int num = getNum(s);
                stack.push(num);     
            }
        }
        //最后将栈内所有元素相加即可
        int res = 0;
        while(!stack.isEmpty()){
            res += stack.poll();
        }
        return res;
    }
    //根据当前的起点索引i寻找一个完整的数字
    public int getNum(String s){
        int num = 0;
        for(; i < s.length(); i++){
            if(s.charAt(i) == ' ') continue;
            if(s.charAt(i) < '0' || s.charAt(i) > '9') break;
            num = num * 10 + s.charAt(i) - '0';
        }
        return num;
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值