7.栈的应用-四则运算算术表达式求解(算符优先法)

来源:互联网 发布:淘宝参数是什么意思 编辑:程序博客网 时间:2024/05/17 01:15

1.理论

表达式求解在程序编译器设计和科学计算器设计中是最基本的一个问题。

这里我们简化模型为简单的四则运算求解,如下:

1+223*309+(9-48/25)

输入的数字称为操作数

输入的运算符为‘+’、‘-’、‘*’、‘/’四则元素符

输入括号‘(’、')'称为界限符

任何一个算术表达式都是由操作数、运算符和界限符构成的。


那么这里为了方便,我们把运算符和界限符统称为算符。这里我们采用所谓的“算符优先法”

比如说对于1+2*3+2-3/5我们人为计算的时候,肯定是先计算*/再计算+-,现在让计算机计算这个表达式,那么必然要模拟这种求值的先后顺序,假设两个从左向右相邻的运算符分别为θ1、θ2,则他们的优先级表如下



这样我们计算的时候只需要查表即可得到两个相邻的算符的优先级,这里我们从左向右遍历表达式,显然对于表达式1+223*(309-48/25)只是凭借当前的1+233是不能确定是否要计算的,必须继续遍历直到1+223*309+才能确定要计算223*309,这很明显要使用栈结构。设计算法如下:

构建两个栈,一个保存操作数,一个保存算符,从左向右遍历表达式

1.遇到数字,压入操作数栈

2.遇到算符则比较栈顶元素和算符的优先级

1).栈顶元素比算符的优先级低,算符压栈

2).栈顶元素与操作符的优先级相同,证明是配对的()或##,栈顶算符出栈

3).栈顶元素比算符的优先级高,算符出栈1个,操作数出栈2个,计算完的结果压入操作数栈


结合算法,理解优先级表的构建原因:

1.这里在运算表达式的开头和结尾计算时都加入一个#,#的优先级最低,标志计算的开始和结束。

2.注意括号()和井号##比较结果为=,当这两者对比时,直接将(出栈表明一个()内的内容已经计算完成,准备进行接下来的计算

3.)和(、#和)、(和#都是不能配对的,配对则说明表达式不合法


在算法实现的过程中还要注意

1.在没有完成计算之前操作数栈或算符栈为空则说明表达式不合法,完成计算以后算符栈不为空或操作数栈长度不为1则表达式不合法

2.除数为0则表达式不合法


2.核心程序


优先级表函数

/** *功能:判断两个操作符的优先级 *参数:operator1--操作符1 *operator2--操作符2 *返回:操作符1优先级大于操作符2--LEVEL_BIGGER *操作符1优先级小于操作符2--LEVEL_SMALLER *操作符1优先级等于操作符2--LEVEL_SAME *操作符1操作符2对比不合法--LEVEL_INVALID *其他:2014/04/17 By Jim Wen Ver1.0 *说明:这里'+'和'-','*'和'/'在实际四则运算时的优先顺序 *是相同的,所以这里的优先级判定时设置两个操作符级别 *列表,一个操作符列表是把另一个操作符级别列表中的相 *同级别的操作符的顺序做了颠倒**/LEVEL_TYPE CompareLevel(char operator1, char operator2){charlevelTable1[] = {'#', '(', '+', '-', '*', '/', ')'};charlevelTable2[] = {'#', ')', '-', '+', '/', '*', '('};intnTable1Index1, nTable1Index2;intnTable2Index1, nTable2Index2;//不合法情况判断if ((operator1==')' && operator2=='(') ||(operator1=='#' && operator2==')') ||(operator1=='(' && operator2=='#')){return LEVEL_INVALID;}//判断相等情况if ((operator1=='(' && operator2==')') ||(operator1=='#' && operator2=='#')){return LEVEL_SAME;}//判断两个操作符在两个优先级表中的位置nTable1Index1 = nTable1Index2 = -1;nTable2Index1 = nTable2Index2 = -1;while (levelTable1[++nTable1Index1] != operator1);while (levelTable1[++nTable1Index2] != operator2);while (levelTable2[++nTable2Index1] != operator1);while (levelTable2[++nTable2Index2] != operator2);//1.判断两个操作符的优先级关系//2.'#' < '+', '-' < '*', '/' //3.两个相同的运算符或同级别的运算符('+'和'-','*'和'/')//对比时应判定为前一个运算符优先级别高,这样才能保证连续//两个相同的运算符或同级别的运算符出现时前一个运算符出栈//完成一次计算if (nTable1Index1-nTable1Index2<0 && nTable2Index1-nTable2Index2<0 ||operator1 == '(' ||operator2 == '('){return LEVEL_SMALLER;}else{return LEVEL_BIGGER;}}

核心计算函数

/** *功能:输入字符串形式的表达式,计算表达式结果 *参数:pExpression--字符串形式的表达式 *pResult--表达式求解结果 *返回:计算成功--真(表达式合法) *计算不成功--假 *其他:2014/04/17 By Jim Wen Ver1.0**/JWArray_BOOL CalcExpression(char *pExpression, double *pResult){JWArray*pStackOperator, *pStackNum;char*pCur;JWArrayElemeOp, eNum1, eNum2, eResult;JWArray_BOOLbResult;pCur= pExpression;bResult= JWARRAY_TRUE;//创建运算符栈和操作数栈pStackOperator= JWArrayCreate(10, 10);pStackNum= JWArrayCreate(10, 10);//初始化操作符栈,入栈'#'表示计算的开始和结束的'#'对应JWArrayPush(pStackOperator, (JWArrayElem)'#');//遍历算术表达式字符串while(pCur[0] != '\0'){if (JWARRAY_FALSE == IsOperator(pCur[0]))//操作数直接压入操作数栈{JWArrayPush(pStackNum, (JWArrayElem)atof(pCur));MoveToNext(&pCur);}else{//获取运算符栈顶元素JWArrayGetTop(pStackOperator, &eOp);switch (CompareLevel((char)eOp, pCur[0])){case LEVEL_INVALID:bResult= JWARRAY_FALSE;break;case LEVEL_SMALLER:JWArrayPush(pStackOperator, (JWArrayElem)(pCur[0]));//运算符压入运算符栈MoveToNext(&pCur);break;case LEVEL_SAME:JWArrayPop(pStackOperator, NULL);//脱掉括号('('和')','#'和'#')MoveToNext(&pCur);break;case LEVEL_BIGGER://出栈操作数和操作符if(JWARRAY_FALSE == JWArrayPop(pStackOperator, &eOp) ||    JWARRAY_FALSE == JWArrayPop(pStackNum, &eNum2) ||   JWARRAY_FALSE == JWArrayPop(pStackNum, &eNum1)){//出栈错误(输入不合法时)bResult= JWARRAY_FALSE;break;}//计算出错if (JWARRAY_FALSE == Calc((double)eNum1, (char)eOp, (double)eNum2, (double *)&eResult)){bResult= JWARRAY_FALSE;break;}JWArrayPush(pStackNum, eResult);//注意这里不移动到下一个操作数或运算符,即出栈计算直到能够计算的计算完毕才再移动break;}}//操作符比较过程出现非法,跳出循环,返回计算错误if (JWARRAY_FALSE == bResult){break;}}//如果运算符栈不为空或操作数栈长度不等于1则运算表达式不合法if (JWArrayGetLength(pStackOperator) != 0 ||JWArrayGetLength(pStackNum) != 1){bResult= JWARRAY_FALSE;}else{JWArrayGetTop(pStackNum, (JWArrayElem *)pResult);}//销毁运算符栈和操作数栈JWArrayDestroy(pStackOperator);JWArrayDestroy(pStackNum);return bResult;}

3.程序说明

1.注意这里的优先级表函数的构造技巧,如果严格按照我们推导出的表来直接比较的话,函数效率太低。采用如下方法

1).这里我们先剔除不合法的配对[)和(、#和)、(和#]

2).再剔除相等的配对[括号()和井号##],剩下的就是小于和大于的情况。

3).判断两个操作符的优先级关系时有'#' < '+', '-' < '*', '/',两个相同的运算符或同级别的运算符('+'和'-','*'和'/')对比时应判定为前一个运算符优先级别高,这样才能保证连续两个相同的运算符或同级别的运算符出现时前一个运算符出栈完成一次计算,即大于的情况比较复杂,我们直接考虑θ1<θ2的情况,显然在θ1、θ2满足'#' < '+', '-' < '*', '/'或θ1=‘(’或θ2='('时即可判断θ1<θ2。

2.这里表达式的不合法只考虑了在输入数字和运算符的情况,没有考虑输入其他不合法的字符如字母等时的情况


测试程序运行结果如下



完整程序下载链接

原创,转载请注明来自http://blog.csdn.net/wenzhou1219

0 0
原创粉丝点击