表达式求值算法总结

表达式求值算法

表达式求值,一般采用栈和队列的方式来求值,下面介绍表达式求值的两种算法。

方法一、使用两个栈,一个为操作符栈OPTR(operator),一个是操作数栈OPND(operand)
算法过程:
当输入 3 * ( 4 - 1 * 2 ) + 6 / ( 1 + 1 )时,为简单方便,我们输入时,按照字符的顺序一个一个的处理,比如

ch = getchar()

然后根据ch 的值判断。

  • ch 是数字,直接压入操作数栈OPND
  • ch'(',直接入栈OPTR;若 ch')',若OPTROPND 非空,弹出OPTR的栈顶操作符,弹出OPND栈顶的两个操作数,做运算,然后见个结果压入栈OPND,直到弹出的OPTR栈顶元素时')';
  • ch 是操作符(比如+, -, *, /),如果OPTR栈顶元素是 (,直接入栈OPTR,如果不是'('OPTR栈非空且栈顶元素操作符的优先级大于ch,那么弹出OPTR的栈顶操作符,并弹出OPND中栈顶的两个元素,做运算,将运算结果入栈OPND,此时,重复这一步操作;否则将ch入栈OPTR
  • ch为EOF,说明表达式已经输入完成,判断OPTR是否为空,若非空,一次弹出OPTR栈顶操作符,并与OPND栈顶两个元素做运算,将运算结果入栈OPND,最后表达式的结果即OPND的栈底元素。

以表达式3 * ( 4 - 1 * 2 ) + 6 / ( 1 + 1 )为例,计算过程如下所示:

OPTROPNDch备注
33
*3*
*,(3(
*,(3,44
*,(,-3,4-
*,(,-3,4,11
*,(,-,*3,4,1*
*,(,-,*3,4,1,22
*,(,-3,4,2)OPND弹出2和1,OPTR弹出*,计算结果入栈OPND
*,(3,2)OPND弹出2和4,OPTR弹出-,计算结果入栈OPND
*3,2)OPTR栈顶弹出的是)
+6+OPTR栈顶元素优先级大于ch,将OPND栈的3和2与OPTR的*运算,结果入栈OPND,ch入栈OPTR
+6,66
+,/6,6/
+,/,(6,6(
+,/,(6,6,11
+,/,(,+6,6,1+
+,/,(,+6,6,1,11
+,/,(6,6,2)OPND的1和1,与OPTR的+运算,结果入栈OPND
+,/6,6,2)
+6,3表达式已经输入完成,OPTR非空,继续计算。OPND的2和6,OPTR的/运算
9计算结果

通过上述的计算过程,写出伪代码如下所示:

void GetExpress(Stack * OPTR, Stack * OPND)
{
    char ch;
    while ((ch = getchar ()) != EOF) {
        if (IsDigit (ch)) {
            PushStack (OPND, ch);
        }
        else if (ch == '(')
            PushStack (OPTR, ch);
        else if (ch == ')') {
            while (!IsStackEmpty(OPTR)) {
                PopStack (OPTR, op);
                if (op == ')')
                    break;
                PopStack (OPND, num2);
                PopStack (OPND, num1);
                res = Calc (num1, num2, op);
                PushStack (OPND, res);
            }
        }
        else if (ch == '+' || ch == '-'
            || ch == '*' || ch == '/') {
            while (!IsStackEmpty (OPTR) && GetTop (OPTR)!='(' && GetTop (OPTR)>ch) {
                PopStack (OPTR, op);
                PopStack (OPND, num2);
                PopStack (OPND, num1);
                res = Calc (num1, num2, op);
                PushStack (OPND, res);
            }
            if (IsStackEmpty (OPTR) || GetTop(OPTR)=='(')
                PushStack (OPTR, ch);
        }
    }
}
// 当表达式输入完成后,需要对OPTR栈和OPND中的元素进行运算
int GetValue(Stack * OPTR, Stack * OPND)
{
    while (!IsStackEmpty (OPTR)) {
        PopStack (OPTR, op);
        PopStack (OPND, num2);
        PopStack (OPND, num1);
        res = Calc (num1, num2, op);
        PushStack (OPND, res);
    }
    // 最后的操作数栈OPND栈顶元素即是表达式的值
    return GetTop(OPND);
}

PS: 上面没有指出表达式非法的情况

方法二:采用中缀表达式的方法,求取表达式的中缀表达式,借用一个操作符栈OPTR和中缀表达式队列Queue,求取中缀表达式,然后对中缀表达式求值。

求取中缀表达式

  • ch 是数字,直接入队列Queue;
  • ch 是操作符(+, -, *, /),如果OPTR栈顶元素是(或者OPTR 为空,直接入栈OPTR;若OPTR非空,且栈顶元素的优先级大于ch,先出栈,且将出栈元素入队Queue,直到栈顶元素小于ch,然后将ch入栈;否则直接将ch入栈;
  • ch(,直接入栈 OPTR;
  • ch),出栈并入队列,直到出栈元素是 (,若栈为空且没有(出现,说明表达式非法。
  • 当表达式输入完成时,判断栈是否为空,若非空,依次弹栈并入队列

求取中缀表达式的值,需要借用一个栈

  • 若队列非空,出队q_ele;如果出队元素q_ele是数字,入栈S;否则取出栈顶两个元素,与q_ele这个操作符左运算,运算结果入栈S
  • 最后的结果为栈顶元素

仍以表达式3 * ( 4 - 1 * 2 ) + 6 / ( 1 + 1 )为例,计算过程如下:

计算中缀表达式:

OPTRQueuech备注
33
*3*栈为空,直接入栈
*,(3(
*,(3,44
*,(,-3,4-栈顶是’(‘
*,(,-3,4,11
*,(,-,*3,4,1*栈顶元素-优先级小于*,直接入栈
*,(,-,*3,4,1,22
*3,4,1,2,*,-)依次出栈并入队,知道出栈元素是’)’
+3,4,1,2,*,-,*+栈顶元素*优先级大于+,出栈入队,然后+入栈
+3,4,1,2,*,-,*,66
+,/3,4,1,2,*,-,*,6/
+,/,(3,4,1,2,*,-,*,6(
+,/,(3,4,1,2,*,-,*,6,11
+,/,(,+3,4,1,2,*,-,*,6,1+
+,/,(,+3,4,1,2,*,-,*,6,1,11
+,/3,4,1,2,*,-,*,6,1,1,+)依次出栈并入队,知道出栈元素是’)’
3,4,1,2,*,-,*,6,1,1,+,/,+表达式已经输入完成,将栈依次出栈并入队

此时中缀表达式为3,4,1,2,*,-,*,6,1,1,+,/,+,队列左边是头,右边是尾。

计算中缀表达式的值:

QueueSq_ele备注
3,4,1,2,*,-,*,6,1,1,+,/,+
4,1,2,*,-,*,6,1,1,+,/,+33
1,2,*,-,*,6,1,1,+,/,+3,44
2,*,-,*,6,1,1,+,/,+3,4,11
*,-,*,6,1,1,+,/,+3,4,1,22
-,*,6,1,1,+,/,+3,4,2*将栈顶2和1,对操作符*做运算,并将结果入栈
*,6,1,1,+,/,+3,2-将栈顶2和4,对操作符-做运算,并将结果入栈
6,1,1,+,/,+6*将栈顶2和3,对操作符*做运算,并将结果入栈
1,1,+,/,+6,66
1,+,/,+6,6,11
+,/,+6,6,1,11
/,+6,6,2+1+1=2
+6,3/6/2=3
9+6+3=9,计算结果

根据以上操作过程,写出伪代码:

void GetPostExpress (Stack * OPTR, Queue * Q)
{
    while ((ch = getchar ()) != EOF) {
        if (IsDigit (ch))   //判断是否是数字
            PushQueue (Q, ch);
        else if (ch == '(')
            PushStack (OPTR, ch);
        else if (ch == '+' || ch == '-'
            || ch == '*' || ch == '/') {
            if (IsStackEmpty (OPTR) || GetTop (OPTR)=='(')
                PushStack (OPTR, ch);

            while (!IsStackEmpty (OPTR) && GetTop (OPTR)!='('
                && GetTop (OPTR)>ch) {
                PopStack (OPTR, op);
                PushQueue (Q, op);
            }
            PushStack (OPTR, ch);
        }

        if (ch == ')') {
            while (!IsStackEmpty (OPTR)) {
                PopStack (OPTR, op);
                if (op == ')')
                    break;

                PushQueue (Q, op);
            }
        }
    }
    // 表达式输入完成 
    while (!IsStackEmpty (OPTR)) {
        PopStack (OPTR, op);
        PushQueue (Q, op);
    }
}

// 计算中缀表达式的值
int GetValue (Queue * Q, Stack * S)
{
    while (!IsQueueEmpty (Q)) {
        PopQueue (Q, ch);
        if (IsDigit (ch))
            PushStack (S, ch);
        else {
            PopStack (S, num2); //需要判断栈是否为空
            PopStack (S, num1);
            res = Calc (num1, num2, ch);
            PushStack (S, res);
        }
    }
    return GetTop (S);  //栈顶元素就是表达式的结果
}
  • 31
    点赞
  • 102
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猫步旅人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值