表示式求值演算法總結
阿新 • • 發佈:2018-12-27
表示式求值演算法
表示式求值,一般採用棧和佇列的方式來求值,下面介紹表示式求值的兩種演算法。
方法一、使用兩個棧,一個為操作符棧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); //棧頂元素就是表示式的結果 }