牛客刷题---表达式求值

94 阅读4分钟

一、题目描述

表达式求值_牛客题霸_牛客网 (nowcoder.com)

二、解题思路

根据运算法则我们可以知道,优先计算括号里面的,其次计算乘法和除法,同等优先级的话需要从左到右顺序计算。因此本问题我们不仅需要解决优先级的问题,以及括号的问题。

在此之前,可以去搜索了解一下后缀表达式。

首先我们定义两个栈:

  • 数据栈nums[]:存放所有数字
  • 操作栈ops[]:存放除数字外的符号,包括+-*/和括号

接着对字符串从前往后依次遍历,这时候会遇到一下几种字符:

  • 空格:跳过

  • 数字:把数字字符转换为整型数字,并压入nums[]栈中

    • 如果是个位数,则可以直接入栈,但是我们需要的是不管几位数都能进行运算。
    • 我们可以每次记暂存值num = 0
    • 当读取到一个数字s[i]时,更新num = num*10 + (s[i] - '0'),这样子就完成了字符转数字,且保证了能够整体取出连续数字。
    • 那么什么时候压入栈中呢?那就是下一个字符是字符串末尾,或下一个字符不是数字的时候,我们就可以把num这个数字压进nums[]中了(压栈后需将num清零)
  • 左括号(:直接压入ops[]就可以了,作为括号内计算结束的标志

  • 右括号):此时可以把括号内的计算完成,每次ops[]出栈一个运算符,nums[]就出栈两个数字来进行运算,并把计算结果重新压入栈中,一直持续到从ops[]读到一个'('为止。最后把左括号出栈。

  • 运算符:前面解决了括号问题,这里就需要解决+-*/优先级的问题了

    • 我们首先把'#'字符压入ops[]栈中,其本身没有意义,但是我们会赋予它的优先级为最低的。
    • 当我们准备将一个运算符s[i]压入栈中的时候,我们需要先将s[i]与栈顶运算符ops[opsTop]的级别进行比较,如果s[i]的级别低于栈顶元素的时候,我们就先将栈顶运算符出栈,同时nums[]也出栈两个数据计算结果,计算完成后把数字进栈。当级别高于栈顶元素的时候,就可以直接压栈了。
    • 那怎么实现相等优先级从左往右计算呢?所谓的从左往右计算,就是说前面的先计算完成,我才可以计算。因此,s[i]不可以压栈的情况是s[i]的级别小于或等于栈顶元素的级别,在此条件下,我们都会先把前面的计算完成后,才会把s[i]压入栈中
    • 举个例子:"2 + 3 * 1 + 2"
    • nums[] = {2},ops[] = {#},读+,因为+的级别高于#,因此可以直接压栈;*级别高于+,可以直接入栈。此时nums[] = {2, 3, 1}, ops[] ={#, + , *}
    • 继续遍历字符串得到 + ,但是+级别低于*,因此会将''出栈,计算结果。‘’出栈完成后,+的级别和+号相等,此时应该从左往右算,因此会继续对ops[]和nums[]出栈,计算结果。
    • 当+级别大于栈顶元素时,才会将+入栈,此时nums[] = {5}, ops[] = {#, +}

三、代码实现

long nums[100];   //最好使用long型,因为两个int相加相乘可能会超出范围
char ops[100];
int numsTop = -1;
int opsTop = -1;

// 运算符优先级
int priority(char c) {
    if (c == '+' || c == '-')
        return 1;
    else if (c == '*' || c == '/')
        return 2;

    return 0;   //'#'或'('的优先级
}

// 出栈计算
long calc(char c) {
    // 出栈两个数字
    int a = nums[numsTop--];
    int b = nums[numsTop--];
    int res = 0;

    // 计算结果
    switch (c) {
        case '+':   res = a + b;    break;
        case '-':   res = b - a;    break;    //注意是  b - a
        case '*':   res = a * b;    break;
        case '/':   res = b / a;    break;   //注意是  b / a
        default:    break;
    }
    return res;
}

int solve(char* s ) {
    ops[opsTop++] = '#'; // 默认#为最低优先级
    int i = 0;
    int num = 0;
    long tmp;

    while (s[i] != '\0') {
        // 遇到空格,直接跳过
        while (s[i] == ' ')
            i++;

        // 遇到数字字符
        if (s[i] >= '0' && s[i] <= '9') {
            num = num * 10 + (s[i] - '0');

            // 到达末尾或后面不是数字,则数字入栈
            if (s[i + 1] == '\0' || (s[i + 1] < '0' || s[i + 1] > '9')) {
                nums[++numsTop] = num;
                num = 0;    //清零,方便下一个数的读取
            }
            // 处理下一个字符
            i++;
            continue;
        }
        // 遇到左括号
        else if (s[i] == '(') {
            // 直接入栈
            ops[++opsTop] = s[i];
            // 处理下一个字符
            i++;
            continue;
        }
        // 遇到右括号
        else if (s[i] == ')') {
            // 出栈计算
            while (ops[opsTop] != '(') {
                tmp = calc(ops[opsTop--]);
                // 计算结果入栈
                nums[++numsTop] = tmp;
            }
            // 左括号出栈
            opsTop--;
            // 处理下一个字符
            i++;
            continue;
        }
        // 遇到运算符
        else {
            // 如果当前运算符优先级小于或等于栈顶优先级,则出栈计算
            //相等优先级时,需要从左往右算,因此也要把前面的计算好后才能将新的运算符压栈
            while (priority(s[i]) <= priority(ops[opsTop])) {
                tmp = calc(ops[opsTop--]);
                // 计算结果入栈
                nums[++numsTop] = tmp;
            }
            // 将当前运算符入栈
            ops[++opsTop] = s[i];
            // 处理下一个字符
            i++;
        }
    }

    // 处理剩余运算符
    while (opsTop > 0) { 
        tmp = calc(ops[opsTop--]);
        nums[++numsTop] = tmp;
    }

    //返回最终运算结果
    return nums[0];
}