【算法题】基础计算器的不同实现方式

本篇作者耗时一天半,整理理解,刚开始尝试自己的思路直接上手,手搓计算器,结果倒下了。。。。只能从头来,看了很多大佬的思路讲解,佩服佩服。


基础计算器

224. 基本计算器https://leetcode.cn/problems/basic-calculator/

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

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


方法一:(标准解法:运算符优先级比较+中缀转后缀+后缀运算)

1. 运算符优先级比较

cpp 复制代码
int operatorPrecedence(char ch)
    {
        struct opPD
        {
            char _op;
            int _pd;
        };
        static opPD arr[] = { {'+', 1},{'-', 1},{'*', 2},{'/', 2} };
        for (auto& e : arr)
        {
            if (e._op == ch)
            {
                return e._pd;
            }
        }
         assert(false);
        return -1;
    }

2. 中缀转后缀

cpp 复制代码
void toRPN(const string& s, size_t& i, vector<string>& v)
    {
        stack<char> st;
        while (i < s.size())
        {
            if (isdigit(s[i]))
            {
                // 运算数输出
                string num;
                while (i < s.size() && isdigit(s[i]))
                {
                    num += s[i];
                    ++i;
                }
                v.push_back(num);
            }
            else
            {
                if (s[i] == '(')
                {
                    // 递归⽅式处理括号中的⼦表达式
                    ++i;
                    toRPN(s, i, v);
                }
                else if (s[i] == ')')
                {
                    ++i;
                    //栈中的运算符全部输出
                    while (!st.empty())
                    {
                        v.push_back(string(1, st.top()));
                        st.pop();
                    }
                    // 结束递归
 
                    return;
                }
                else
                {
// 1、如果栈为空或者栈不为空且当前运算符⽐栈顶运算符优先级⾼,则当前运算符⼊栈
// 2、如果栈不为为空且⽐栈顶运算符优先级低或相等,说明栈顶的运算符可以运算了,
// 3、输出栈顶运算符,当前运算符继续⾛前⾯遇到运算符的逻辑
                  if (st.empty() || operatorPrecedence(s[i])>operatorPrecedence(st.top()))
                    {
                        st.push(s[i]);
                        ++i;
                    }
                    else
                    {
                        v.push_back(string(1, st.top()));
                        st.pop();
                    }
                }
            }
        }
        // 栈中的运算符全部输出
 
        while (!st.empty())
        {
            v.push_back(string(1, st.top()));
            st.pop();
        }
    }

3. 后缀运算

cpp 复制代码
int evalRPN(const vector<string>& tokens) {
        stack<long long> s;
        for (size_t i = 0; i < tokens.size(); ++i)
        {
            const string& str = tokens[i];
            // str为数字
            if (!("+" == str || "-" == str || "*" == str || "/" == str))
            {
                s.push(stoll(str));
            }
            else
            {
                // str为操作符
                long long right = s.top();
                s.pop();
                long long left = s.top();
                s.pop();
                    // 运算符
                     switch (str[0])
                {
                case '+':
                    s.push(left + right);
                    break;
                case '-':
                    s.push(left - right);
                    break;
                case '*':
                    s.push(left * right);
                    break;
                case '/':
                    s.push(left / right);
                    break;
                }
            }
        }
        return s.top();
    }

4. 详细代码

cpp 复制代码
class Solution {
public:
    int operatorPrecedence(char ch)
    {
        struct opPD
        {
            char _op;
            int _pd;
        };
        static opPD arr[] = { {'+', 1},{'-', 1},{'*', 2},{'/', 2} };
        for (auto& e : arr)
        {
            if (e._op == ch)
            {
                return e._pd;
            }
        }
         assert(false);
        return -1;
    }
    void toRPN(const string& s, size_t& i, vector<string>& v)
    {
        stack<char> st;
        while (i < s.size())
        {
            if (isdigit(s[i]))
            {
                // 运算数输出
                string num;
                while (i < s.size() && isdigit(s[i]))
                {
                    num += s[i];
                    ++i;
                }
                v.push_back(num);
            }
            else
            {
                if (s[i] == '(')
                {
                    // 递归⽅式处理括号中的⼦表达式
 
                    ++i;
                    toRPN(s, i, v);
                }
                else if (s[i] == ')')
                {
                    ++i;
                    //栈中的运算符全部输出
                    while (!st.empty())
                    {
                        v.push_back(string(1, st.top()));
                        st.pop();
                    }
                    // 结束递归
 
                    return;
                }
                else
                {// 1、如果栈为空或者栈不为空且当前运算符⽐栈顶运算符优先级⾼,则当前运算符⼊栈
                // 2、如果栈不为为空且⽐栈顶运算符优先级低或相等,说明栈顶的运算符可以运算了,
                // 输出栈顶运算符,当前运算符继续⾛前⾯遇到运算符的逻辑
                    if (st.empty() || operatorPrecedence(s[i]) > operatorPrecedence(st.top()))
                    {
                        st.push(s[i]);
                        ++i;
                    }
                    else
                    {
                        v.push_back(string(1, st.top()));
                        st.pop();
                    }
                }
            }
        }
        // 栈中的运算符全部输出
 
        while (!st.empty())
        {
            v.push_back(string(1, st.top()));
            st.pop();
        }
    }
    int evalRPN(const vector<string>& tokens) {
        stack<long long> s;
        for (size_t i = 0; i < tokens.size(); ++i)
        {
            const string& str = tokens[i];
            // str为数字
            if (!("+" == str || "-" == str || "*" == str || "/" == str))
            {
                s.push(stoll(str));
            }
            else
            {
                // str为操作符
                long long right = s.top();
                s.pop();
                long long left = s.top();
                s.pop();
                    // 运算符
                     switch (str[0])
                {
                case '+':
                    s.push(left + right);
                    break;
                case '-':
                    s.push(left - right);
                    break;
                case '*':
                    s.push(left * right);
                    break;
                case '/':
                    s.push(left / right);
                    break;
                }
            }
        }
        return s.top();
    }
    int calculate(string s) {
        // 1、去除所有空格,否则下⾯的⼀些逻辑没办法处理
        std::string news;
        news.reserve(s.size());
        for (auto ch : s)
        {
            if (ch != ' ')
                news += ch;
        }
        s.swap(news);
        news.clear();
        // 2、将所有的负数-x转换为0-x 
        for (size_t i = 0; i < s.size(); ++i)
        {
            if (s[i] == '-' && (i == 0 || (!isdigit(s[i - 1]) && s[i - 1] != ')')))
                news += "0-";
            else
                news += s[i];
        }
        size_t i = 0;
        vector<string> v;
        // 后缀表达式进⾏运算
         toRPN(news, i, v);
    
 
        return evalRPN(v);
        // 中缀表达式转成后缀表达式
    }
};

方法二:(处理括号+用栈判断括号内运算符)

详细代码:

cpp 复制代码
class Solution {
public:
    int calculate(string s) {
        int ans = 0, sign = 1, i = 0, n = s.size();
        stack<int> stk;
        stk.push(1);
        while (i < n) {
            if (s[i] == ' ') ++i;
            else if (s[i] == '+') {
                sign = stk.top();
                ++i;
            }
            else if (s[i] == '-') {
                sign = -stk.top();
                ++i;
            }
            else if (s[i] == '(') {
                stk.push(sign);
                ++i;
            }
            else if (s[i] == ')') {
                stk.pop();
                ++i;
            }
            else {
                long num = 0;
                while (i < n && '0' <= s[i] && s[i] <= '9') {
                    num = num * 10 + s[i] - '0';
                    ++i;
                }
                ans += sign * num;
            }
        }
        return ans;
    }
};

方法三:(用栈存储正负数据)

详细代码:

cpp 复制代码
int calculate(string s) {
    stack<int> stk;
    // 记录算式中的数字
    int num = 0;
    // 记录 num 前的符号,初始化为 +
    char sign = '+';
    for (int i = 0; i < s.size(); i++) {
        char c = s[i];
        // 如果是数字,连续读取到 num
        if (isdigit(c)) 
            num = 10 * num + (c - '0');
        // 如果不是数字,就是遇到了下一个符号,
        // 之前的数字和符号就要存进栈中
        if (!isdigit(c) || i == s.size() - 1) {
            switch (sign) {
                case '+':
                    stk.push(num); break;
                case '-':
                    stk.push(-num); break;
            }
            // 更新符号为当前符号,数字清零
            sign = c;
            num = 0;
        }
    }
    // 将栈中所有结果求和就是答案
    int res = 0;
    while (!stk.empty()) {
        res += stk.top();
        stk.pop();
    }
    return res;
}

扩展:(乘除法)

cpp 复制代码
for (int i = 0; i < s.size(); i++) {
    char c = s[i];
    if (isdigit(c)) 
        num = 10 * num + (c - '0');

    if (!isdigit(c) || i == s.size() - 1) {
        switch (sign) {
            int pre;
            case '+':
                stk.push(num); break;
            case '-':
                stk.push(-num); break;
            // 只要拿出前一个数字做对应运算即可
            case '*':
                pre = stk.top();
                stk.pop();
                stk.push(pre * num);
                break;
            case '/':
                pre = stk.top();
                stk.pop();
                stk.push(pre / num);
                break;
        }
        // 更新符号为当前符号,数字清零
        sign = c;
        num = 0;
    }
}

基础计算器ll

227. 基本计算器 IIhttps://leetcode.cn/problems/basic-calculator-ii/

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

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

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

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

cpp 复制代码
class Solution {
public:
    int calculate(string s) {
        stack <long long> a1;
        long long num = 0;
        char sign = '+'; // 初始化为 '+',以便正确处理字符串开头的第一个数字
        string st;
        
        // 1. 去除空格
        for(auto e : s) {
            if(!isspace(e)) {
                st.push_back(e);
            }
        }

        for(int i = 0; i < st.size(); i++) {
            if(isdigit(st[i])) {
                num = num * 10 + st[i] - '0';
            }
            
            // 当遇到操作符,或者到达字符串末尾时,处理前一个操作符 sign 和当前的数字 num
            if(!isdigit(st[i]) || i == st.size() - 1) {
                long long pre;
                switch(sign) {
                    case '+':
                        a1.push(num);
                        break; 
                    case '-':
                        a1.push(-num);
                        break; 
                    case '*':
                    {
                        pre = a1.top(); 
                        a1.pop();
                        a1.push(pre * num);
                        break; 
                    }                  
                    case '/':
                    {
                        pre = a1.top();
                        a1.pop();
                        a1.push(pre / num);
                        break; 
                    }  
                }
                sign = st[i]; // 更新当前操作符,留给下一次循环使用
                num = 0;      // 重置 num,准备计算下一个操作数
            }
        }
        
        // 3. 累加栈中所有结果
        long long add = 0;
        while(!a1.empty()) {
            add = add + a1.top();
            a1.pop();
        }
        return (int)add;
    }
};

相关推荐
.道阻且长.5 小时前
2.LeetCode算法习题讲解--双指针--复写零
算法·leetcode·职场和发展
·薯条大王5 小时前
经济实惠玩云服务器|一台云服务器多人共用,子账号配置教程
java·linux·运维·服务器·汇编·c++·python
To_OC7 小时前
LC 438 找到所有字母异位词:暴力超时后,我靠滑动窗口一招搞定
javascript·算法·leetcode
Forever Nore9 小时前
学完C语言力扣第一题做不来正常吗
数据结构·算法
hansang_IR10 小时前
【题解】LC:倍增 / 区间并查集(Range Parallel Unionfind)
c++·算法·并查集
东东最爱敲键盘10 小时前
day7.c++继承
c++
沫璃染墨11 小时前
《从零入门Linux系统篇(十五):系统工具篇·六——GDB调试器详解:从程序执行控制到高级调试技巧》
linux·运维·服务器·c语言·c++
吃着火锅x唱着歌11 小时前
Effective C++ 学习笔记 条款40 明智而审慎地使用多重继承
c++·笔记·学习
Tisfy11 小时前
LeetCode 3731.找出缺失的元素:哈希 / 排序
算法·leetcode·哈希算法·排序·哈希表