['LeetCode']表达式求值

Eval Expression(栈)

表达式求值是指给定一个表达式字符串,求得表达式最后的值。
例如给定表达式: 3 + 2 * (4 + 1) ,通过表达式求值后得到的值为13

这里之所以写LeetCode是因为做LC中题目时碰到类似题目,所以把这个通用问题写一写以作记录。

解决方法(使用栈求值):

这里只介绍使用栈的版本,其他方法还有待发掘。

其实解决思路是挺明确的,大体是用两个栈分别存储操作符操作数,然后顺序解析字符串,关键在于操作符的操作。这里分为两种情况:

  1. 操作符的优先级:如果遇到的操作符优先级大于栈顶操作符的优先级,则操作符入栈。
  2. 的问题:如果遇到反括号,则除了遵守优先级规定外,遇到要一起消除。

循环完成后,根据栈中的内容进行计算,最后得到的数为结果。

注:这里没有考虑小数情况(包括除法)和式子中有符号的情况。

实现

虽然程序并不复杂,思路也很直接。但是在实现中需要注意不要使用某个数来代替优先级,如’‘+’的优先级是1‘ 等。下面会做详细说明:

首先来看优先级表:

    Priorities[ '+' ][ '-' ] = '>' ;
    Priorities[ '+' ][ '+' ] = '>' ;
    Priorities[ '+' ][ '*' ] = '<' ;
    Priorities[ '+' ][ '/' ] = '<' ;
    Priorities[ '+' ][ '(' ] = '<' ;
    Priorities[ '+' ][ ')' ] = '>' ;

    Priorities[ '-' ][ '-' ] = '>' ;
    Priorities[ '-' ][ '+' ] = '>' ;
    Priorities[ '-' ][ '*' ] = '<' ;
    Priorities[ '-' ][ '/' ] = '<' ;
    Priorities[ '-' ][ '(' ] = '<' ;
    Priorities[ '-' ][ ')' ] = '>' ;

    Priorities[ '*' ][ '-' ] = '>' ;
    Priorities[ '*' ][ '+' ] = '>' ;
    Priorities[ '*' ][ '*' ] = '>' ;
    Priorities[ '*' ][ '/' ] = '>' ;
    Priorities[ '*' ][ '(' ] = '<' ;
    Priorities[ '*' ][ ')' ] = '>' ;

    Priorities[ '/' ][ '-' ] = '>' ;
    Priorities[ '/' ][ '+' ] = '>' ;
    Priorities[ '/' ][ '*' ] = '>' ;
    Priorities[ '/' ][ '/' ] = '>' ;
    Priorities[ '/' ][ '(' ] = '<' ;
    Priorities[ '/' ][ ')' ] = '>' ;

    Priorities[ '(' ][ '+' ] = '<' ;
    Priorities[ '(' ][ '-' ] = '<' ;
    Priorities[ '(' ][ '*' ] = '<' ;
    Priorities[ '(' ][ '/' ] = '<' ;
    Priorities[ '(' ][ '(' ] = '<' ;
    Priorities[ '(' ][ ')' ] = '=' ;

可以看到这个优先级是没有传递性的,例如‘(’ 和 ‘+’,对应表中都是’<’。举个栗子:

栈顶是’(‘,遇上‘+’,应该将+入栈,则 ‘+’ > ‘(’
栈顶是’+’, 遇上‘(’,应该将(入栈,则 ‘+’ < ‘(‘

所以用数字来表示优先级是不合适的。

下面的的代码借鉴这个博客
写的比较清楚

#include <string>
#include <iostream>
#include <unordered_map>
using namespace std ;

// 运算符优先级表
unordered_map< char , unordered_map< char , char > > Priorities ;

// 初始化运算符优先级定义数据
void InitPriorities( )
{
    Priorities[ '+' ][ '-' ] = '>' ;
    Priorities[ '+' ][ '+' ] = '>' ;
    Priorities[ '+' ][ '*' ] = '<' ;
    Priorities[ '+' ][ '/' ] = '<' ;
    Priorities[ '+' ][ '(' ] = '<' ;
    Priorities[ '+' ][ ')' ] = '>' ;

    Priorities[ '-' ][ '-' ] = '>' ;
    Priorities[ '-' ][ '+' ] = '>' ;
    Priorities[ '-' ][ '*' ] = '<' ;
    Priorities[ '-' ][ '/' ] = '<' ;
    Priorities[ '-' ][ '(' ] = '<' ;
    Priorities[ '-' ][ ')' ] = '>' ;

    Priorities[ '*' ][ '-' ] = '>' ;
    Priorities[ '*' ][ '+' ] = '>' ;
    Priorities[ '*' ][ '*' ] = '>' ;
    Priorities[ '*' ][ '/' ] = '>' ;
    Priorities[ '*' ][ '(' ] = '<' ;
    Priorities[ '*' ][ ')' ] = '>' ;

    Priorities[ '/' ][ '-' ] = '>' ;
    Priorities[ '/' ][ '+' ] = '>' ;
    Priorities[ '/' ][ '*' ] = '>' ;
    Priorities[ '/' ][ '/' ] = '>' ;
    Priorities[ '/' ][ '(' ] = '<' ;
    Priorities[ '/' ][ ')' ] = '>' ;

    Priorities[ '(' ][ '+' ] = '<' ;
    Priorities[ '(' ][ '-' ] = '<' ;
    Priorities[ '(' ][ '*' ] = '<' ;
    Priorities[ '(' ][ '/' ] = '<' ;
    Priorities[ '(' ][ '(' ] = '<' ;
    Priorities[ '(' ][ ')' ] = '=' ;

    // 不存在操作符1是)和 操作符2 比较的情况
    // 因为 ) 会迫使之前的操作符进行运算。
    // 直到遇到匹配的“(”操作符,双双被消除掉
    // 所以下面的数据无意义。
    Priorities[ ')' ][ '+' ] = '?' ;
    Priorities[ ')' ][ '-' ] = '?' ;
    Priorities[ ')' ][ '*' ] = '?' ;
    Priorities[ ')' ][ '/' ] = '?' ;
    Priorities[ ')' ][ '(' ] = '?' ;
    Priorities[ ')' ][ ')' ] = '?' ;

}

// 计算2个操作数 加减乘除 的结果。
float Calculate( float Operand1 , float Operand2 , char Operator )
{
    float Ret = 0 ;
    if ( Operator == '+' )
    {
        Ret = Operand1 + Operand2 ;
    }
    else if ( Operator == '-' )
    {
        Ret = Operand1 - Operand2 ;
    }
    else if ( Operator == '*' )
    {
        Ret = Operand1 * Operand2 ;
    }
    else if ( Operator == '/' )
    {
        Ret = Operand1 / Operand2 ;
    }

    return Ret ;
}

// 计算 加减,不带括号的表达式
float EvaluateExpression( const string& str )
{
    vector< float > Operands ; // 操作数栈,也可以用 stack< float >
    vector< char > Operators ; // 操作符栈,也可以用 stack< char >
    float OperandTemp = 0 ;

    char LastOperator = 0 ;  // 记录最后遇到的操作符

    for ( size_t i = 0 , size = str.size( ) ; i < size ; ++i )
    {
        const char& ch = str[ i ] ;

        if ( '0' <= ch && ch <= '9' )
        {   // 读取一个操作数
            OperandTemp = OperandTemp * 10 + ch - '0' ;
        }
        else if ( ch == '+' || ch == '-' || ch == '*' || ch == '/' ||
            ch == '(' || ch == ')' )
        {
            // 有2种情况 是没有操作数需要入栈保存的。
            // 1 当前操作符是 “(”。(的左边的操作符已经负责操作数入栈了。
            // 2 上一次遇到的操作符是“)”。)本身会负责操作数入栈,)后面紧跟的操作符不需要再负责操作数入栈。
            if ( ch != '(' && LastOperator != ')' )
            {
                // 遇到一个操作符后,意味着之前读取的操作数已经结束。保存操作数。
                Operands.push_back( OperandTemp ) ;
                // 清空,为读取下一个操作符做准备。
                OperandTemp = 0 ;
            }

            // 当前遇到的操作符作为操作符2,将和之前遇到的操作符(作为操作符1)进行优先级比较
            const char& Opt2 = ch ;

            for ( ; Operators.size( ) > 0 ; )
            {
                // 比较当前遇到的操作符和上一次遇到的操作符的优先级
                const char& Opt1 = Operators.back( ) ;
                char CompareRet = Priorities[ Opt1 ][ Opt2 ] ;
                if ( CompareRet == '>' ) 
                {   // 如果操作符1 大于 操作符2 那么,操作符1应该先计算

                    // 取出之前保存的操作数2
                    float Operand2 = Operands.back( ) ;
                    Operands.pop_back( ) ;

                    // 取出之前保存的操作数1
                    float Operand1 = Operands.back( ) ;
                    Operands.pop_back( ) ;

                    // 取出之前保存的操作符。当前计算这个操作符,计算完成后,消除该操作符,就没必要保存了。
                    Operators.pop_back( ) ;

                    // 二元操作符计算。并把计算结果保存。
                    float Ret = Calculate( Operand1 , Operand2 , Opt1 ) ;
                    Operands.push_back( Ret ) ;
                }
                else if ( CompareRet == '<' ) 
                {   // 如果操作符1 小于 操作符2,说明 操作符1 和 操作符2 当前都不能进行计算,
                    // 退出循环,记录操作符。
                    break;
                }
                else if ( CompareRet == '=' )
                {
                    // 操作符相等的情况,只有操作符2是“)”,操作数1是“(”的情况,
                    // 弹出原先保存的操作符“(”,意味着“(”,“)”已经互相消掉,括号内容已经计算完毕
                    Operators.pop_back( ) ;
                    break;
                }

            } // end for

            // 保存当前遇到操作符,当前操作符还缺少右操作数,要读完右操作数才能计算。
            if ( Opt2 != ')' )
            {
                Operators.push_back( Opt2 ) ;
            }

            LastOperator = Opt2 ;
        }

    } // end for


    /*
    上面的 for 会一面遍历表达式一面计算,如果可以计算的话。
    当遍历完成后,并不代表整个表达式计算完成了。而会有2种情况:
    1.剩余1个运算符。
    2.剩余2个运算符,且运算符1 小于 运算符2。这种情况,在上面的遍历过程中是不能进行计算的,所以才会被遗留下来。
    到这里,已经不需要进行优先级比较了。情况1和情况2,都是循环取出最后读入的操作符进行运算。
    */
    if ( LastOperator != ')' )
    {
        Operands.push_back( OperandTemp ) ;
    }
    for ( ; Operators.size( ) > 0 ; )
    {
        // 取出之前保存的操作数2
        float Operand2 = Operands.back( ) ;
        Operands.pop_back( ) ;

        // 取出之前保存的操作数1
        float Operand1 = Operands.back( ) ;
        Operands.pop_back( ) ;

        // 取出末端一个操作符
        char Opt = Operators.back( ) ;
        Operators.pop_back( ) ;

        // 二元操作符计算。
        float Ret = Calculate( Operand1 , Operand2 , Opt ) ;
        Operands.push_back( Ret ) ;
    }

    return Operands[ 0 ] ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值