表达式求值C++代码模板(有完整实现思路,代码附有详细注释)

表达式求值

表达式求值这个知识点在最近几年的找工作笔试、面试,考研机试,各种算法竞赛笔试中出现的频率越来越高了;但是以前从来没有见过这种题要想在笔试面试中写出来不是一件简单的事情,网络上面大部分代码不够精炼,不方便理解和背诵模板;本篇博客提供了表达式求值的一道经典模板题,从理解表达式求值思路到整个代码模板,方便大家理解和背诵;旨在帮助广大笔试面试的朋友轻松应对此类问题。

题目

给定一个表达式,其中运算符仅包含 + , − , ∗ , / +,-,*,/ +,,,/(加 减 乘 整除),可能包含括号,请你求出表达式的最终值。

注意

  1. 数据保证给定的表达式合法。
  2. 题目保证符号 − - 只作为减号出现,不会作为负号出现,例如, − 1 + 2 -1+2 1+2, ( 2 + 2 ) ∗ ( − ( 1 + 1 ) + 2 ) (2+2)*(-(1+1)+2) (2+2)((1+1)+2) 之类表达式均不会出现。
  3. 题目保证表达式中所有数字均为正整数。
  4. 题目保证表达式在中间计算过程以及结果中,均不超过 2 31 − 1 2^{31}−1 2311
  5. 题目中的整除是指向 0取整,也就是说对于大于 0的结果向下取整,例如 5/3=1,对于小于 0的结果向上取整,例如 5 / ( 1 − 4 ) = − 1 5/(1−4)=−1 5/(14)=1
  6. C++Java中的整除默认是向零取整;Python中的整除//默认向下取整,因此Python e v a l ( ) eval() eval()函数中的整除也是向下取整,在本题中不能直接使用。
    原题链接

表达式求值思路(建议理解背诵)

对于给定表达式字符串,扫描一遍字符串,每一个字符可能出现如下几种情况:

  1. 数字
  2. 左括号
  3. 右括号
  4. 运算符

需要两个栈实现该算法

  1. 操作数栈(保存操作数)
  2. 操作符栈(保存还不能确定运算顺序的操作符)

求解步骤

  1. 若碰到操作数,直接将该操作数压入操作数栈中
  2. 若碰到左括号,直接将左括号压入操作符栈中
  3. 若碰到右括号,弹出操作符栈中的操作符直到碰到左括号为止
  4. 若碰到操作符,弹出操作符栈中优先级 大于或者等于 大于或者等于 大于或者等于该操作符的元素(若碰到到左括号或者栈为空应该停止)
  5. 若扫描完字符串后,运算符栈不为空,重复执行 e v a l ( ) eval() eval()函数直到栈为空

手动模拟

下面对中缀表达式 ( 2 ∗ 1 + 6 ) ∗ ( 5 − 3 ) / 4 ( 2 * 1 + 6 ) * ( 5 - 3 ) / 4 (21+6)(53)/4 手动模拟此算法的操作过程
步骤1
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第1张图片
步骤2
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第2张图片
步骤3
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第3张图片
步骤4
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第4张图片
步骤5
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第5张图片
步骤6
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第6张图片
步骤7
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第7张图片
步骤8
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第8张图片
步骤9
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第9张图片
步骤10
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第10张图片
步骤11
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第11张图片
步骤12
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第12张图片
步骤13
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第13张图片
步骤14
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第14张图片
步骤15
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第15张图片
步骤16
表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第16张图片

实现代码(附注释)

#include
#include
#include
#include
using namespace std;

//定义操作符栈和操作数栈
stack<int> num;
stack<char> op;

//eval函数的功能是弹出栈顶两个操作数a和b,弹出栈顶的操作符c,根据操作符,将a与b做运算得到结果x再压回栈中
void eval(){
    int b = num.top();
    num.pop();
    int a = num.top();
    num.pop();
    char c = op.top();
    op.pop();
    int x;
    if(c == '+') x = a + b;
    else if(c == '-') x = a - b;
    else if(c == '*') x = a * b;
    else x = a / b;
    num.push(x);
}

int main(){
    //读入表达式字符串
    string s;
    cin >> s;

    //定义四个操作符的优先级; 加减的优先级比乘除低,这里定义加减是1,乘除是2
    map<char, int> mp = {{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};

    //从左到右扫描一遍字符串
    for(int i = 0; i < s.size(); i++){
        //四种情况分别对应该字符是数字,左括号,右括号,操作符
        if(isdigit(s[i])){
            //operand存储操作数,因为操作数在字符串里面可能有连续几位
            int j = i, operand = 0;
            //这段代码不要害怕,实际上是将字符串"1234"转换成数字1234的代码
            while(j < s.size() && isdigit(s[j])){
                operand = operand * 10 + s[j] - '0';
                j++;
            }
            num.push(operand); //将操作数压入栈中
            i = j - 1; //for循环里面会执行i++,所以这里j需要减去1
        }else if(s[i] == '('){
            op.push(s[i]); //左括号则压栈
        }else if(s[i] == ')'){
            //右括号则一直匹配,直到碰到左括号位置
            while(op.top() != '(') eval();
            op.pop();
        }else{
            //若栈不为空且没有碰到左括号且栈顶元素的优先级大于等于该操作符的优先级
            while(op.size() && op.top() != '(' && mp[op.top()] >= mp[s[i]])
                eval();
            op.push(s[i]); //将操作符压入栈中
        }
    }
    //操作到运算符栈空为止
    while(op.size()) eval();
    //最后运算符栈顶保存的元素即为最后表达式的值
    cout << num.top() << endl;
    return 0;
}

代码注意点

  1. e v a l ( ) eval() eval() 函数实现 x x x = = = a a a o p op op b b b 时候,根据栈的后进先出原则,所以先弹出的栈顶元素是操作数 b b b,后弹出的栈顶元素是操作数 a a a,这里一定不要写反了,否则是错误的计算结果(想一想 x x x = = = a a a − - b b b x x x = = = b b b − - a a a 的计算结果肯定是不相同的)。
  2. 扫描完表达式字符串之后,可能操作符栈和操作数栈不为空,此时还要继续执行 e v a l ( ) eval() eval() 函数直到栈为空(代码写到最后这个点极其容易忽略)。
  3. m a p < c h a r , i n t > map map<char,int> 也可以写成 u n o r d e r e d − m a p < c h a r , i n t > unordered-map unorderedmap<char,int>
  4. 表达式求值C++代码模板(有完整实现思路,代码附有详细注释)_第17张图片
    这段代码初学者看起来会觉得难,可以静下心来好好想一下;最后记得要写 i = j − 1 i = j - 1 i=j1,因为for循环执行会自动执行 i + + i++ i++
  5. 表达式求值的代码稍作修改就是中缀表达式转后缀表达式的代码,读者可以思考一下为什么,后期笔者将会更新此篇博客,感兴趣的话记得点个关注吧!
  6. 中缀表达式转后缀表达式详细思路和实现代码请见博客 —> 博客传送门

如果觉得对你有所帮助的话,麻烦你点赞,收藏,关注我,后期还会更新博客

你可能感兴趣的:(模板题,算法,c++,算法,数据结构)