如何快速搓出一个计算器

刷题发现手搓计算器的题居然都被用在面试手撕上了,基础的比如带括号的加减乘除,再恶心一点的给你上个右结合的乘方,中缀转后缀肯定能做,但代码又臭又长还有不少细节,你能保证隔一段时间去面试的时候能当场快速撕出来吗?很蓝的啦

学栈的时候大部分人应该都做过中缀转后缀,虽然我不清楚别人是怎么觉得的,但至少对我来说这个东西实在难以感性理解,毫无理由地告诉你一系列规则照做,更多的是强行背下来导致过一段时间就忘了。

那么首先要知道为什么中缀转后缀会难以理解。这个东西实际上是LR(1)的一个特例,那些又臭又长的规则实际上是在根据下一个输入符号判断做移进还是规约。用LR(1)的状态机概念去理解中缀转后缀的话就能知道这些规则本质上是在做什么了。但LR(1)本身就不适合拿来做算法题,本地跑了一下状态机,带括号的加减乘除与乘方计算器有11条生成式,状态一共高达40种,虽然不排除有冯诺依曼再世能在脑袋里把这玩意想明白,但我觉得正常人应该觉得还不如去把中缀转后缀算法背下来。

其实可以发现在只需要实现加减乘除、括号和乘方的情况下,文法不光是LR(1)的,甚至是LL(1)的,也就是说直接递归下降就可以做了。当然直接用BNF的话光加减或者乘除操作就会得出来E->E+T这种左递归玩意儿,但用EBNF就可以把左递归直接干掉,并且把11个生成式缩成5个:

复制代码
expression=term, { ("+" | "-"), term }
term=factor, { ("*" | "/"), factor}
factor=[ "+" | "-" ], pow
pow=num, [ "^", pow ]
num=number | "(", expression, ")"

然后就是简单的看图说话了,这样就从背一堆容易出错的规则转成了简单理解五条直观的生成式,连吗喽也可以在面试时直接撕出来(

另外如果中缀转后缀的话也是同理,只是把返回值从int改成void,在Parser里维护一个流,返回前往流里面写一下就行

cpp 复制代码
int qpow(int a, int b) {
    int ans = 1;
    while (b) {
        if (b & 1) ans = ans * a;
        a = a * a; b >>= 1;
    }
    return ans;
}
class Parser {
    std::string s;
    int pos;
public:
    Parser(const std::string& s) {
        this->s = s;
        pos = 0;
    }
    char peek() {
        while (pos < s.length() && s[pos] == ' ') ++pos;
        return pos < s.length()? s[pos]: '\0';
    }
    char consume() {
        return s[pos++];
    }

    int parse_expression() {
        int ans = parse_term();
        while (peek() == '+' || peek() == '-') {
            char op = consume();
            if (op == '+') {
                ans += parse_term();
            } else {
                ans -= parse_term();
            }
        }
        return ans;
    }

    int parse_term() {
        int ans = parse_factor();
        while (peek() == '*' || peek() == '/') {
            char op = consume();
            if (op == '*') {
                ans *= parse_factor();
            } else {
                ans /= parse_factor();
            }
        }
        return ans;
    }

    int parse_factor() {
        int ans = 0;
        if (peek() == '+') {
            consume();
            ans = parse_pow();
        } else if (peek() == '-') {
            consume();
            ans = -parse_pow();
        } else {
            ans = parse_pow();
        }
        return ans;
    }

    int parse_pow() {
        int ans = parse_num();
        if (peek() == '^') {
            consume();
            ans = qpow(ans, parse_pow());
        }
        return ans;
    }

    int parse_num() {
        int ans = 0;
        if (peek() == '(') {
            consume();
            ans = parse_expression();
            consume();
        } else {
            while (peek() >= '0' && peek() <= '9') {
                ans = ans * 10 + (consume() - '0');
            }
        }
        return ans;
    }
};

class Solution {
public:
    int calculate(string s) {
        Parser p(s);
        return p.parse_expression();
    }
};
相关推荐
xiaobaibai15312 分钟前
智慧交通中目标检测 mAP↑28%:陌讯多模态融合算法实战解析
人工智能·算法·目标检测·计算机视觉·目标跟踪·视觉检测
战争热诚20 分钟前
基于transformer的目标检测——匈牙利匹配算法
算法·目标检测·transformer
计算机科研圈25 分钟前
ICCV 2025 | EPD-Solver:西湖大学发布并行加速扩散采样算法
人工智能·算法·语言模型·自然语言处理·数据挖掘·iccv
Yzxs0091 小时前
【8月优质EI会议合集|高录用|EI检索稳定】计算机、光学、通信技术、电子、建模、数学、通信工程...
大数据·人工智能·算法·计算机视觉·信息与通信
佳航张2 小时前
选择排序原理与C语言实现详解
算法
hans汉斯2 小时前
【建模与仿真】二阶邻居节点信息驱动的节点重要性排序算法
人工智能·python·算法·分类·数据挖掘·排序算法·xca
不会学习的小白O^O3 小时前
蛇形卷积介绍
算法
nov.5813 小时前
嵌入式学习日志——数据结构(一)
数据结构·学习·算法
橙小花3 小时前
C语言:函数指针、二级指针、常量指针常量、野指针
c语言·数据结构·算法
西猫雷婶4 小时前
python学智能算法(三十一)|SVM-Slater条件理解
人工智能·python·算法·机器学习·支持向量机