📜  门| GATE CS 2019 |第 64 题(1)

📅  最后修改于: 2023-12-03 15:28:38.271000             🧑  作者: Mango

题目介绍

这道题目来自 Gate CS 2019 的第 64 题,涉及到对于运算符和优先级的理解。题目如下:

给定一个算术表达式的字符串,其中运算符仅包括加号和乘号,数字可以是单个数字或多位数字。例如,表达式 1 + 2 + 3 和 2 * 3 * 4 是有效的表达式,而表达式 1 + 2 * 3 和 2 * 3 + 4 * 5 + 是无效的表达式。需要通过对运算符的分析来计算出表达式的结果。

你需要实现一个函数,接受字符数组作为输入,计算并返回表达式的结果,若输入的表达式不符合规则,则应该返回 -1。

函数原型为:int evaluate(char *expression);

例如,对于字符串 "2 * 3 + 4",你的程序应该返回 10,因为这个表达式等价于 2 * (3 + 4) = 14。

解题思路

本题思路比较简单,可以通过使用栈来实现。从左至右扫描表达式字符串,如果是数字,就将其压入栈中;如果是运算符,就弹出栈顶的两个数字,并按照运算符的优先级进行计算,将结果重新压入栈中。

在本题中,加法和乘法的优先级不同,因此需要对其进行判断。如果当前的运算符是乘法,就需要再弹出一个数字,并将两个数字乘起来。最后,当整个字符串扫描完毕后,栈中仅剩下一个数字,即为表达式的值。

如果在扫描过程中发现输入的表达式不符合规则,则应该返回 -1。

代码实现

下面是使用栈实现的 C++ 代码:

#include <iostream>
#include <stack>
using namespace std;

// 判断一个字符是否为数字
bool isNumericChar(char c) {
  return (c >= '0' && c <= '9') ? true : false;
}

// 将字符数组形式的数字转换为整型数字
int charToNum(char c) {
  return c - '0';
}

// 计算表达式的值
int evaluate(char *expression) {
  stack<int> nums;  // 用于存储数字的栈
  stack<char> ops;  // 用于存储运算符的栈
  for (int i = 0; expression[i]; i++) {
    if (expression[i] == ' ') {
      continue;  // 忽略空格
    }
    else if (expression[i] == '(') {
      ops.push(expression[i]);
    }
    else if (isNumericChar(expression[i])) {
      // 如果是数字,则将连续的数字组成一个整数,然后压入数字栈中
      int num = 0;
      while (isNumericChar(expression[i])) {
        num = num * 10 + charToNum(expression[i]);
        i++;
      }
      i--;  // 因为循环结束后 i 多余了 1
      nums.push(num);
    }
    else if (expression[i] == '+' || expression[i] == '*') {
      // 如果是运算符,则将栈顶的两个数字弹出并进行运算,
      // 然后将结果压回数字栈中,最后将当前运算符压回运算符栈中
      while (!ops.empty() && ops.top() != '(' &&
             ((expression[i] == '*' && ops.top() == '+') || (expression[i] == '*' && ops.top() == '*'))) {
        int num2 = nums.top();
        nums.pop();
        int num1 = nums.top();
        nums.pop();
        char op = ops.top();
        ops.pop();
        if (op == '+') {
          nums.push(num1 + num2);
        }
        else if (op == '*') {
          nums.push(num1 * num2);
        }
      }
      ops.push(expression[i]);
    }
    else if (expression[i] == ')') {
      // 如果是右括号,则将栈顶的两个数字弹出并进行运算,
      // 然后将结果压回数字栈中,直到遇到了左括号为止,
      // 最后将左右括号都弹出
      while (!ops.empty() && ops.top() != '(') {
        int num2 = nums.top();
        nums.pop();
        int num1 = nums.top();
        nums.pop();
        char op = ops.top();
        ops.pop();
        if (op == '+') {
          nums.push(num1 + num2);
        }
        else if (op == '*') {
          nums.push(num1 * num2);
        }
      }
      ops.pop(); // 弹出左括号
    }
    else {
      // 如果输入的表达式不符合规则,则返回 -1
      return -1;
    }
  }
  // 如果这个时候操作符栈不为空,还需要执行一遍计算
  while (!ops.empty()) {
    int num2 = nums.top();
    nums.pop();
    int num1 = nums.top();
    nums.pop();
    char op = ops.top();
    ops.pop();
    if (op == '+') {
      nums.push(num1 + num2);
    }
    else if (op == '*') {
      nums.push(num1 * num2);
    }
  }
  // 最后栈中剩下的唯一一个数字即为表达式的值
  return nums.top();
}

注:本代码采用了算法竞赛入门经典(第 2 版)中的实现方式。

总结

在解决函数实现的问题时,需要注意细节,特别是在输入的表达式出现不符合规则的情况下,需要返回 -1。这也是本题比较难的地方之一。当然,借助栈这种数据结构,结合运算符的优先级,还是比较好实现的,特别是当我们选择较为合适的实现方式时,算法的时间复杂度可以做到 O(n)。