青训营X豆包MarsCode 技术训练营:简单四则运算解析器c++题解 | 豆包MarsCode AI 刷题

53 阅读4分钟

问题描述

小F面临一个编程挑战:实现一个基本的计算器来计算简单的字符串表达式的值。该字符串表达式有效,并可能包含数字(0-9)、运算符+-及括号()。注意,字符串中不包含空格。除法运算应只保留整数结果。请实现一个解析器计算这些表达式的值,且不使用任何内置的eval函数。


测试样例

样例1:

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

样例2:

输入:expression = "3+4*5/(3+2)"
输出:7

样例3:

输入:expression = "4+2*5-2/1"
输出:12

样例4:

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

样例5:

输入:expression = "2*(5+5*2)/3+(6+8*3)"
输出:40

解题思路

根据题目可以知道输入的表达式是一个中缀表达式,可以使用运算符优先级的处理以及双栈(操作数栈和运算符栈)实现表达式的计算。 核心思路是:

  1. 遍历表达式

    • 遇到数字:直接压入操作数栈。
    • 遇到左括号:压入运算符栈。
    • 遇到右括号:弹出运算符栈直到左括号,逐步计算结果。
    • 遇到运算符:根据优先级判断是否需要弹栈并计算,最后压入当前运算符。
  2. 优先级规则

    • 运算符优先级 */ 高于 +-
    • 遇到括号时,确保括号内的表达式先被计算。
  3. 最后清理:遍历完成后,清空运算符栈并完成计算。

代码

#include <cctype>
#include <iostream>
#include <string>
#include <unordered_map>
#include <cstring>
using namespace std;

const int N = 100010;
int num[N], op[N], ttn, tto;

void eval() {
    int b = num[ttn--];
    int a = num[ttn--];
    char c = op[tto--];
    int x;
    if(c == '+')
        x = a + b;
    else if(c == '-')
        x = a - b;
    else if(c == '*')
        x = a * b;
    else
        x = a / b;
    num[++ttn] = x;
}

int solution(std::string expression) {
    unordered_map<char, int> pr{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};
    for(int i = 0; i < expression.size(); i++) {
        char c = expression[i];
        if(isdigit(c)) {
            int x = c - '0';
            num[++ttn] = x;
        } else if(c == '(') {
            op[++tto] = c;
        } else if(c == ')') {
            while(op[tto] != '(')
                eval();
            tto--;
        } else {
            while(tto != 0 && op[tto] != '(' && pr[op[tto]] >= pr[c])
                eval();
            op[++tto] = c;
        }
    }
    while(tto != 0)
        eval();
    return num[ttn];
}

int main() {
    // You can add more test cases here
    std::cout << (solution("1+1") == 2) << std::endl;
    std::cout << (solution("3+4*5/(3+2)") == 7) << std::endl;
    std::cout << (solution("4+2*5-2/1") == 12) << std::endl;
    std::cout << (solution("(1+(4+5+2)-3)+(6+8)") == 23) << std::endl;
    return 0;
}

关键代码解释

1. 运算符优先级设置

unordered_map<char, int> pr{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};
  • unordered_map 用于记录运算符的优先级。
  • 数值越高表示优先级越高。
  • 加和减的优先级是1,乘和除的优先级是2

2. 数字处理

if(isdigit(c)) {
    int x = c - '0';
    num[++ttn] = x;
}
  • 检测到当前字符是数字,将其转换为整数并压入操作数栈 num

3. 括号处理

  • 左括号:简单压栈。
else if(c == '(') {
    op[++tto] = c;
}
  • 右括号:弹出运算符栈,逐步计算直到遇到左括号。
else if(c == ')') {
    while(op[tto] != '(')
        eval();
    tto--;
}

4. 运算符处理

  • 比较当前运算符和栈顶运算符的优先级,决定是否先计算栈中的运算。
while(tto != 0 && op[tto] != '(' && pr[op[tto]] >= pr[c])
    eval();
op[++tto] = c;

5. 计算逻辑

void eval() {
    int b = num[ttn--];
    int a = num[ttn--];
    char c = op[tto--];
    int x;
    if(c == '+') x = a + b;
    else if(c == '-') x = a - b;
    else if(c == '*') x = a * b;
    else x = a / b;
    num[++ttn] = x;
}
  • 从操作数栈中弹出两个操作数 ab,从运算符栈中弹出一个运算符 c,根据运算符执行计算并将结果压入操作数栈。

时间空间复杂度分析

时间复杂度

  • 表达式遍历

    • 每个字符被访问一次,总复杂度为 O(n)。
  • 操作栈处理

    • 每次入栈和出栈操作为 O(1)。
    • eval 函数执行 O(1) 操作,最坏情况下调用 O(n) 次。
  • 总时间复杂度为 O(n)

空间复杂度

  • 主要存储了两个栈:

    • 操作数栈 num:大小为 O(n)。
    • 运算符栈 op:大小为 O(n)。
  • 总空间复杂度为 O(n)

优化点

  1. 支持多位数处理

当前代码支持的是个位数的计算,如果是多位数,可以使用一个while循环来提取数值

if(isdigit(c)) { 
    int x = 0; 
    while(i < expression.size() && isdigit(expression[i])) { 
        x = x * 10 + (expression[i] - '0'); 
        i++; 
    } 
    i--; // 回退1位,因为for循环还会执行i++ 
    num[++ttn] = x; 
}
  1. 支持空格处理
  • 可以添加对空格的跳过逻辑:
if (isspace(c)) continue;

总结

这是一道简单的表达式求值题,可以使用自定义栈来进行解题。个人感觉自定义栈挺方便的,也可以做到一些c++库里的栈做不到的事,可以根据实际需求优化存储方式和操作逻辑,从而减少不必要的开销。