表达式求值,一般采用栈和队列的方式来求值,下面介绍表达式求值的两种算法。
方法一、使用两个栈,一个为操作符栈OPTR(operator)
,一个是操作数栈OPND(operand)
算法过程:
当输入 3 * ( 4 - 1 * 2 ) + 6 / ( 1 + 1 )
时,为简单方便,我们输入时,按照字符的顺序一个一个的处理,比如
ch = getchar()
然后根据ch
的值判断。
ch
是数字,直接压入操作数栈OPND
; ch
是'('
,直接入栈OPTR
;若 ch
是')'
,若OPTR
和 OPND
非空,弹出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 )
为例,计算过程如下所示:
OPTR | OPND | ch | 备注 |
3 | 3 | ||
* | 3 | * | |
*,( | 3 | ( | |
*,( | 3,4 | 4 | |
*,(,- | 3,4 | - | |
*,(,- | 3,4,1 | 1 | |
*,(,-,* | 3,4,1 | * | |
*,(,-,* | 3,4,1,2 | 2 | |
*,(,- | 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,6 | 6 | |
+,/ | 6,6 | / | |
+,/,( | 6,6 | ( | |
+,/,( | 6,6,1 | 1 | |
+,/,(,+ | 6,6,1 | + | |
+,/,(,+ | 6,6,1,1 | 1 | |
+,/,( | 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 )
为例,计算过程如下:
计算中缀表达式:
OPTR | Queue | ch | 备注 |
3 | 3 | ||
* | 3 | * | 栈为空,直接入栈 |
*,( | 3 | ( | |
*,( | 3,4 | 4 | |
*,(,- | 3,4 | - | 栈顶是’(‘ |
*,(,- | 3,4,1 | 1 | |
*,(,-,* | 3,4,1 | * | 栈顶元素-优先级小于*,直接入栈 |
*,(,-,* | 3,4,1,2 | 2 | |
* | 3,4,1,2,*,- | ) | 依次出栈并入队,知道出栈元素是’)’ |
+ | 3,4,1,2,*,-,* | + | 栈顶元素*优先级大于+,出栈入队,然后+入栈 |
+ | 3,4,1,2,*,-,*,6 | 6 | |
+,/ | 3,4,1,2,*,-,*,6 | / | |
+,/,( | 3,4,1,2,*,-,*,6 | ( | |
+,/,( | 3,4,1,2,*,-,*,6,1 | 1 | |
+,/,(,+ | 3,4,1,2,*,-,*,6,1 | + | |
+,/,(,+ | 3,4,1,2,*,-,*,6,1,1 | 1 | |
+,/ | 3,4,1,2,*,-,*,6,1,1,+ | ) | 依次出栈并入队,知道出栈元素是’)’ |
3,4,1,2,*,-,*,6,1,1,+,/,+ | 表达式已经输入完成,将栈依次出栈并入队 |
此时中缀表达式为3,4,1,2,*,-,*,6,1,1,+,/,+
,队列左边是头,右边是尾。
计算中缀表达式的值:
Queue | S | q_ele | 备注 |
3,4,1,2,*,-,*,6,1,1,+,/,+ | |||
4,1,2,*,-,*,6,1,1,+,/,+ | 3 | 3 | |
1,2,*,-,*,6,1,1,+,/,+ | 3,4 | 4 | |
2,*,-,*,6,1,1,+,/,+ | 3,4,1 | 1 | |
*,-,*,6,1,1,+,/,+ | 3,4,1,2 | 2 | |
-,*,6,1,1,+,/,+ | 3,4,2 | * | 将栈顶2和1,对操作符*做运算,并将结果入栈 |
*,6,1,1,+,/,+ | 3,2 | - | 将栈顶2和4,对操作符-做运算,并将结果入栈 |
6,1,1,+,/,+ | 6 | * | 将栈顶2和3,对操作符*做运算,并将结果入栈 |
1,1,+,/,+ | 6,6 | 6 | |
1,+,/,+ | 6,6,1 | 1 | |
+,/,+ | 6,6,1,1 | 1 | |
/,+ | 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); //栈顶元素就是表达式的结果 }