算法刷题笔记 表达式求值(C++实现)

69 篇文章 3 订阅
63 篇文章 1 订阅

题目描述

  • 给定一个表达式,其中运算符仅包含 +,-,*,/(加 减 乘 整除),可能包含括号,请你求出表达式的最终值。
  • 注意
    • 数据保证给定的表达式合法。
    • 题目保证符号-只作为减号出现,不会作为负号出现,例如,-1+2,(2+2)*(-(1+1)+2) 之类表达式均不会出现。
    • 题目保证表达式中所有数字均为正整数。
    • 题目保证表达式在中间计算过程以及结果中,均不超过2^31−1
    • 题目中的整除是指向0取整,也就是说对于大于0的结果向下取整,例如5/3=1,对于小于0的结果向上取整,例如5/(1−4)=−1。C++和Java中的整除默认是向零取整;Python中的整除//默认向下取整,因此Python的eval()函数中的整除也是向下取整,在本题中不能直接使用。

输入格式

  • 共一行,为给定表达式。

输出格式

  • 共一行,为表达式的结果。

数据范围

  • 表达式的长度不超过10^5

基本思路

  • 这道题是一道经典的算法模板题,即中缀算术表达式求值。这一类模板题中,所采用的运算符基本上都是二元运算符,需要借助的数据结构是栈,利用该数据结构先进后出的特性。
  • 首先,我们分析一下中缀表达式求值的难点是什么,可以发现该问题的难点在于一个算术表达式中,具有不同类型的运算符,有时候甚至带有括号,使得先进行什么运算,然后进行什么运算变得困难,因此该问题的核心就是确定表达式中各个运算的相对顺序。
  • 本题中之所以采用栈这种数据结构,是因为栈中元素的出栈顺序即可表示表达式中各个运算符之间的运算顺序;另外,用一个栈存储运算数,可以记录不同运算数之间参与运算的先后顺序。

实现代码

#include <iostream>       // 使用C++中的cin和cout进行输入输出更加简单
#include <cctype>         // 使用isdigit函数需要导入的头文件
#include <stack>          // 使用STL中自带的栈需要导入的头文件
#include <unordered_map>  // 使用STL中自带的哈希表(字典)需要导入的头文件
using namespace std;

stack<int> numbers;     // 运算优先级越高的数字,越靠近栈顶
stack<char> operators;  // 运算优先级越高的运算,越靠近栈顶
// 以哈希表(相当于Python中的字典)的形式记录不同运算符的优先级(如果有更多的运算符则修改此表即可)
unordered_map<char, int> priority{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};

void get_value(void)
{
    int number2 = numbers.top(); numbers.pop();
    int number1 = numbers.top(); numbers.pop();
    char operation = operators.top(); operators.pop();
    switch(operation)
    {
        case '+' : numbers.push(number1 + number2);break;
        case '-' : numbers.push(number1 - number2);break;
        case '*' : numbers.push(number1 * number2);break;
        case '/' : numbers.push(number1 / number2);break;
    }
}

int main(void)
{
    // 以C++字符串的形式输入中缀算术表达式
    string expression;
    cin >> expression;
    // 遍历整个字符串,确保表达式中的所有运算和数字都存入了栈
    for(int i = 0; i < expression.length(); ++ i)
    {
        // 情况1:读入的字符是数字(使用cctype头文件中的isdigit函数判定),则将其与运算符分开
        if(isdigit(expression[i]))
        {
            // 数字可能是多位整数,因此需要循环判定下一个字符是否也是数字
            int number = 0;
            // 使用条件表达式的短路规则进行判定,即表达式没有遍历完,且当前字符为数字
            while(i < expression.length() && isdigit(expression[i]))
            {
                // 记录当前的数字的值并自增位置
                number = number * 10 + (expression[i] - '0');
                ++ i;
            }
            // 将每一个运算数都压入运算数栈并更新位置
            numbers.push(number);
            -- i;
        }
        // 情况2:读入的字符是左括号,则直接将其入栈即可而无需判定
        else if(expression[i] == '(') operators.push('(');
        // 情况3:读入的字符是右括号,由于括号内优先级高,因此先计算括号内的
        else if(expression[i] == ')') 
        {
            // 持续进行运算,直到找到该右括号对应的左括号,并将左括号出栈
            while(operators.top() != '(') get_value();
            operators.pop();
        }
        // 情况4:读入的字符是二元运算符(加减乘除)
        else 
        {
            // 当运算符栈不为空,且当前遍历到的运算符的优先级不高于栈顶的元素的优先级,则先取出栈顶的元素进行计算
            while(!operators.empty() && priority[expression[i]] <= priority[operators.top()]) get_value();
            // 运算完成后将当前遍历到的运算符入栈
            operators.push(expression[i]);
        }
    }
    // 遍历完了表达式,运算过程可能仍然没有结束,需要继续运算直到运算符栈为空
    while(!operators.empty()) get_value();
    // 输出运算数栈的栈顶元素
    cout << numbers.top();
    return 0;
}
  • isdigit函数:用于判定一个字符是否是数字字符(0-9),使用时需要导入头文件<cctype>
  • 短路原则:在使用&&连接的表示“与”的条件表达式中,只有前一个条件表达式为真,才会继续判定下一个条件表达式,因此在代码情况1中的while语句,并不会出现expression[i]越界的问题。
  • 栈的操作:C++的STL中提供了栈模板类,使用时需要首先导入头文件<stack>。可以使用栈对象的top()函数获取栈顶元素,使用pop()函数弹出栈顶元素(无返回值),使用empty()函数判定栈是否为空(返回布尔值),使用push()函数入栈一个元素。
  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值