['LeetCode']表达式求值
2015-06-30 14:55
387 查看
Eval Expression(栈)
表达式求值是指给定一个表达式字符串,求得表达式最后的值。例如给定表达式: 3 + 2 * (4 + 1) ,通过表达式求值后得到的值为13。
这里之所以写LeetCode是因为做LC中题目时碰到类似题目,所以把这个通用问题写一写以作记录。
解决方法(使用栈求值):
这里只介绍使用栈的版本,其他方法还有待发掘。其实解决思路是挺明确的,大体是用两个栈分别存储操作符和操作数,然后顺序解析字符串,关键在于操作符的操作。这里分为两种情况:
操作符的优先级:如果遇到的操作符优先级大于栈顶操作符的优先级,则操作符入栈。
)的问题:如果遇到反括号,则除了遵守优先级规定外,遇到(要一起消除。
循环完成后,根据栈中的内容进行计算,最后得到的数为结果。
注:这里没有考虑小数情况(包括除法)和式子中有符号的情况。
实现
虽然程序并不复杂,思路也很直接。但是在实现中需要注意不要使用某个数来代替优先级,如’‘+’的优先级是1‘ 等。下面会做详细说明:首先来看优先级表:
[code] 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[ '(' ][ ')' ] = '=' ;
可以看到这个优先级是没有传递性的,例如‘(’ 和 ‘+’,对应表中都是’<’。举个栗子:
栈顶是’(‘,遇上‘+’,应该将+入栈,则 ‘+’ > ‘(’
栈顶是’+’, 遇上‘(’,应该将(入栈,则 ‘+’ < ‘(‘
所以用数字来表示优先级是不合适的。
下面的的代码借鉴这个博客
写的比较清楚
[code]#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 ] ; }
相关文章推荐
- 程序调用存储过程中报错 DB2 SQL Error: SQLCODE=-286, SQLSTATE=42727, SQLERRMC=4096;
- Could not resolve archetype org.apache.maven.archetypes:maven-archetype-webapp:RELEASE from any of t
- 【译】VisionMobile:语音:脱离运营商的业务模式
- 基于实体模型开发主题管理简析
- AngularJS笔记
- php分析MSSQL返回的时间位object,怎样输出时间?
- windows 下apache 以fastcgi形式运行php
- Luajit2.0.4 Build错误
- jdk版本
- 为什么你应该停止使用EventBus
- 根据List中单个对象的某方法进行去重
- Trie树(字典树)(1)
- 跟我一起透彻理解template模板模式
- Android 监听EdText的变化,过滤显示数据
- OGRE在渲染通路中使用顶点程序和片断程序
- JS合并的必要性分析
- struts2 文件下载(修正中文问题)
- Hello,world!
- 使用Eclipse构建Maven项目 (step-by-step)
- linux 硬盘分区,分区,删除分区,格式化,挂载,卸载笔记