top1编程
← 返回题目
题解

简单表达式求值

1 条题解

  • 0
    @ 2026-8-7 12:55:51

    P4831 简单表达式求值(基础)

    解题思路

    第一步,看懂运算优先级。 数学里规定"先乘除、后加减",比如 3+42-6/3,要先算 42=8 和 6/3=2,再算 3+8-2,最后结果等于 9。我们写的程序也要遵守这个顺序,不能从左到右傻傻地直接算。

    第二步,把表达式拆成数字和运算符。 用 cin.getline 读入整行表达式,然后从左到右扫描。每个数字可能是正数也可能是负数(前面带负号),还可能是小数(中间有小数点),要一个不漏地解析出来放进数组 val;运算符放进数组 op。比如 3+42 会得到 val=[3,4,2],op=[+,]。

    第三步,第一遍:先算乘除。 再开两个新数组 v2 和 o2。把第一个数字先放进 v2,然后逐个看运算符:遇到乘号 * 或除号 /,就直接拿它和 v2 里最后放的那个数字算出结果,再放回 v2;遇到加号 + 或减号 -,先把这个运算符存进 o2,把运算符后面的数字存进 v2。这样走完一遍,乘除全部算完,剩下的只有加减。

    第四步,第二遍:再算加减。 现在 v2 里存着若干数字,o2 里存着加减号,正好是"数字、加减号、数字、加减号……"的样子,从左到右依次相加相减,就得到最终答案。例如 3+4*2-6/3:第一遍后 v2=[3,8,2],o2=[+,-],第二遍算 3+8-2=9,输出 9.00。

    第五步,注意各种细节。 负数要能解析,比如 -3+4 表示负三加四,结果是 1;小数要能解析,比如 3.5 的整数部分和小数部分要拼在一起;最后结果用 printf("%.2f") 保留两位小数输出。题目保证表达式长度不超过16,数组开20格就足够装下。

    参考代码

    // 简单表达式求值:只含 + - * / 和数字(可负、可小数),先乘除后加减
    #include <iostream>
    #include <cstring>
    using namespace std;
    
    int main() {
        char s[20];
        double val[20] = {0};   // 解析出的数字
        char op[20];      // 解析出的运算符
        int nc = 0, oc = 0;
        // 读入整行表达式(可能含空格,逐个跳过)
        cin.getline(s, 20);
        int i = 0, len = (int)strlen(s);
        while (i < len) {
            if (s[i] == ' ') { i++; continue; }   // 跳过空格
            // 解析一个数字,可能有正负号和小数点
            int neg = 1;
            if (s[i] == '+' || s[i] == '-') {
                if (s[i] == '-') neg = -1;
                i++;
            }
            double x = 0;
            while (i < len && s[i] >= '0' && s[i] <= '9') {
                x = x * 10 + (s[i] - '0');
                i++;
            }
            if (i < len && s[i] == '.') {  // 小数点后的部分
                i++;
                double p = 0.1;
                while (i < len && s[i] >= '0' && s[i] <= '9') {
                    x += (s[i] - '0') * p;
                    p /= 10;
                    i++;
                }
            }
            val[nc++] = neg * x;
            // 解析运算符
            if (i < len && s[i] != ' ') {
                op[oc++] = s[i];
                i++;
            }
        }
        // 第一遍:先处理乘除,结果压回数组
        double v2[20];
        char o2[20];
        int n2 = 0, o2c = 0;
        v2[n2++] = val[0];
        for (i = 0; i < oc; i++) {
            if (op[i] == '*' || op[i] == '/') {
                if (op[i] == '*') v2[n2 - 1] *= val[i + 1];
                else v2[n2 - 1] /= val[i + 1];
            } else {
                o2[o2c++] = op[i];
                v2[n2++] = val[i + 1];
            }
        }
        // 第二遍:从左到右处理加减
        double res = v2[0];
        for (i = 0; i < o2c; i++) {
            if (o2[i] == '+') res += v2[i + 1];
            else res -= v2[i + 1];
        }
        // 结果保留2位小数
        printf("%.2f\n", res);
        return 0;
    }
    

    复杂度分析

    表达式的长度不超过16,扫描一遍解析数字和运算符只需要 O(16) 即常数时间;第一遍处理乘除、第二遍处理加减,每遍也只需要常数次操作。所以整个程序的时间复杂度是 O(1),空间上几个数组大小都是常数,空间复杂度也是 O(1)。即使表达式中既有正负数又有小数,处理的步骤数也始终不超过表达式的长度。

    • 1