Leetcode(227)——基本计算器 II

解析基本计算器表达式,实现单栈算法,处理加减乘除运算。掌握整数运算优先级,避免内置函数,挑战字符串转整数计算。

Leetcode(227)——基本计算器 II

题目

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

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

你可以假设给定的表达式总是有效的。所有中间结果将在 [−231,231−1][-2^{31}, 2^{31 - 1}][231,2311] 的范围内。

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

示例 1:

输入:s = “3+2*2”
输出:7

示例 2:

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

示例 3:

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

提示:

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

题解

方法:单栈保存数

思路

​​  由于乘除优先于加减计算,因此不妨考虑先进行所有乘除运算,并将这些乘除运算后的整数值放回原表达式的相应位置,则随后整个表达式的值,就等于一系列整数加减后的值。

​​  基于此,我们可以用一个栈,保存这些(进行乘除运算后的)整数的值。对于加减号后的数字,将其直接压入栈中;对于乘除号后的数字,可以直接与栈顶元素计算,并替换栈顶元素为计算后的结果。

​​  具体来说,遍历字符串 sss,并用变量 last\textit{last}last 记录每个数字之前的运算符,对于第一个数字,其之前的运算符视为加号。每次遍历到数字末尾时(即遇到下一个运算符时),根据 last\textit{last}last 来决定计算方式:

  • 加号:将数字压入栈;
  • 减号:将栈顶元素替换为其负值;
  • 乘除号:计算数字与栈顶元素,并将栈顶元素替换为计算结果。

​​  代码实现中,若读到一个运算符,或者遍历到字符串末尾,即认为是遍历到了数字末尾。处理完该数字后,更新 last\textit{last}last 为当前遍历到的运算符。

​​  遍历完字符串 sss 后,将栈中元素累加,即为该字符串表达式的值。

代码实现

我自己写的:

class Solution {
public:
    int calculate(string s) {
        stack<int> S1;  // S1 数字
        char last;      // 记录上一个非加法运算符
        int tmp;        // 记录旧栈顶元素
        bool islastValue = false;   // 上一个入栈的是数还是运算符,是为true,不是为false
        int ans = 0;    // 保存结果
        // 初始化
        // 对于数字:
        //      如果连续将数字入栈则将栈顶元素*10再将 当前字符-'0' 入栈,否则直接将 当前字符-'0' 入栈
        // 对于运算符:
        //      遇到 "+" 就不入栈(后面会默认+),如果是 "-" 则先入栈并等待字符串遍历完或遇到下一个运算符再将栈顶数字变为负值。
        //      如果遇到 "*" 或 "/" 则先存入栈中,并在下一次存储数字时,将栈顶元素取出和当前数字进行运算再放回栈中。
        for(int n = 0; n <= s.length(); n++){   // 比遍历字符串s多循环一次,以确保使用最后一个运算符
            if(n != s.length() && '0'<=s[n] && s[n]<='9'){
                // 两种情况:
                // 1.上一次入栈的是数字:将栈顶元素*10,再将 当前字符-'0' 入栈
                // 2.上一次入栈的是运算符:判断是不是减法,不是则将栈顶元素取出与 当前元素-'0' 进行相应运算
                if(!islastValue)
                    S1.push(s[n]-'0');
                else S1.top() = S1.top()*10-'0'+s[n]; 
                // 注意不能是S1.top()*10+s[n]-'0',S1.top()*10+it这步会发生 int 类型的越界,所以先减再加
                islastValue = true;
            }else if(n == s.length() || s[n] != ' '){   // 防止访问数组越界,所以将 n != s.length() 放在 || 前面
                tmp = S1.top(); // 保存旧的栈顶值
                if(last == '-')
                    S1.top() = -tmp;
                else if(last == '*'){
                    S1.pop();
                    S1.top() = S1.top() * tmp;
                }else if(last == '/'){
                    S1.pop();
                    S1.top() = S1.top() / tmp;
                }
                last = s[n];  // 更新非加法运算符
                islastValue = false;
            }
        }
        // 栈中的剩余数字默认都是加法
        while(!S1.empty()){
            ans += S1.top();
            S1.pop();
        }
        return ans;
    }
};

Leetcode 官方题解:

class Solution {
public:
    int calculate(string s) {
        vector<int> stk;
        char preSign = '+';
        int num = 0;
        int n = s.length();
        for (int i = 0; i < n; ++i) {
            if (isdigit(s[i])) {
                num = num * 10 + int(s[i] - '0');
            }
            if (!isdigit(s[i]) && s[i] != ' ' || i == n - 1) {
                switch (preSign) {
                    case '+':
                        stk.push_back(num);
                        break;
                    case '-':
                        stk.push_back(-num);
                        break;
                    case '*':
                        stk.back() *= num;
                        break;
                    default:
                        stk.back() /= num;
                }
                preSign = s[i];
                num = 0;
            }
        }
        return accumulate(stk.begin(), stk.end(), 0);
    }
};
复杂度分析

时间复杂度O(n)O(n)O(n) ,其中 nnn 是字符串 sss 的长度。因为需要遍历字符串 s 一次,计算表达式的值。
空间复杂度O(n)O(n)O(n),其中 nnn 为字符串 sss 中加法运算对象的个数。空间复杂度主要取决于栈的空间,栈的元素个数不超过 nnn

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值