栈---算符优先算法

【例3-3】表达式求值。表达式求值是程序设计语言编译中的一个基本问题,它的实现是栈应用的一个典型例子。这里介绍“算符优先算法”,这种算法简单直观且使用广泛。
“算符优先算法”是用运算符的优先级来确定表达式的运算顺序,从而对表达式进行求值。在机器内部,任何一个表达式都是由操作数(Operand)、运算符(Operator)和界限符(Delimiter)组成。操作数和运算符是表达式的主要部分,分界符标志了一个表达式的结束。根据表达式的类型,表达式分为三类,即算术表达式、关系表达式和逻辑表达式。为简化问题,我们仅讨论四则算术运算表达式,并且假设一个算术表达式中只包含加、减、乘、除、左圆括号和右圆括号等符号,并假设‘#’是界限符。
要把一个表达式翻译成正确求值的一个机器指令序列,或者直接对表达式求
数据结构(C#语言版)
3.1 栈 85
值,首先要能够正确解释表达式,这需要了解算术四则运算的规则。算术四则运算的规则如下:
(1) 先乘除后加减;
(2) 先括号内后括号外;
(3) 同级别时先左后右。
我们把运算符和界限符统称为算符。根据上述三条运算规则,在任意相继出现的算符θ1和θ2之间至多是下面三种关系之一:
(1)θ1<θ2 θ1的优选权低于θ2;
(2)θ1=θ2 θ1的优选权等于θ2;
(3)θ1>θ2 θ1的优选权高于θ2。
表3-1定义了算符之间的这种优先关系,为了算法简洁,在表达式的最左边也虚设一个‘#’构成整个表达式的一对括号。
表3-1 算符之间的优先级关系 θ2 + - * / ( ) # θ1 + > > < < < > > - > > < < < > > * > > > > < > > / > > > > < > > ( < < < < < = ) > > > > > > # < < < < < =
由表3-1可知:
(1)‘#’的优先级最低,当‘#’=‘#’表示整个表达式结束;
(2)同级别的算符遇到时,左边算符的优先级高于右边算符的优先级,如‘+’与‘+’、‘-’与‘-’、‘+’与‘-’等;
(3)‘(’在左边出现时,其优先级低于右边出现的算符,如‘+’、‘-’、‘*’等,‘(’=‘)’表示括号内运算结束;‘(’在右边出现时,其优先级高于左边出现的算符,如‘+’、‘-’、‘*’等;
(4)‘)’在左边出现时,其优先级高于右边出现的算符,如‘+’、‘-’、‘*’等;‘)’在右边出现时,其优先级低于左边出现的算符,如‘+’、‘-’、‘*’等;
(5)‘)’与‘(’、‘#’与‘)’、‘(’与‘#’之间无优先关系,在表达式中不允许相继出现,如果出现认为是语法错误。
为实现算法,使用两个栈,一个存放算符,叫OPTR,一个存放操作数和运算的结果数,叫OPND。算法思想如下:
(1) 首先置OPND为空,将‘#’入OPTR;
(2) 依次读入表达式中的每个字符,若是操作数则将该字符入OPND,若是算符,则和OPTR栈顶字符比较优先级,若OPTR栈顶字符优先级高,则
数据结构(C#语言版)
3.1 栈 86
将OPND栈中的两个操作数和OPTR栈顶算符出栈,然后将操作结果入OPND;若OPTR栈顶字符优先级低,则将该字符入OPTR;若二者优先级相等,则将OPTR栈顶字符出栈并读入下一个字符。
表达式求值的算法实现如下。本例的算法是处理整数的,也可以对实数等其它数进行处理,只不过把类型改为实数等相应类型即可。
public int EvaluateExpression()
{
SeqStack<char> optr = new SeqStack <char>(20);
SeqStack<int> opnd = new SeqStack <int>(20);
optr.Push(‘#’);
char c = Console.Read();
char theta = 0;
int a = 0;
int b = 0;
while (c != ‘#’)
{
if((c!=’+’) && (c!=’-‘)
&& (c!=’*’) && (c!=’/’)
&& (c!=’(‘) && (c!=’)’))
{
optr.Push(c);
}
else
{
switch(Precede(optr.GetTop(), c))
{
Case ‘<’:
optr.Push(c);
c = Console.Read();
break;
case ‘=’:
optr.Pop();
c = Console.Read();
break;
case ‘>’:
theta = optr.Pop();
a = opnd.Pop();
b = opnd.Pop();
opnd.Push(Operate(a,theta,b));
break;
}
}


你可能感兴趣的:(栈---算符优先算法)