前、中、后缀表达式概述及转换+栈的计算器原理及代码分析(含完整源码)
目錄:
1.前中后綴表達式的概述
2.中序表達式轉前后綴表達式
3.運用棧的后綴表達式實現計算器原理步驟
4.代碼實現和分析
1.前中后綴表達式的概述及相互轉換
- 前綴表達式:運算符位于操作數之前。
- 中綴表達式(波蘭式):首先前中后綴表達式,一般正常寫的(2*4-1)+5-6這種式子稱為中綴表達式。
- 后綴表達式(逆波蘭式):運算符位于操作數之前。
我們平時所看到的中綴表達式,計算機是不能直接拿來運算的,因為計算機不知道該如何計算,然而計算機能知道該如何計算前后綴表達式。這就是前后綴表達式的意義
前綴和后綴表達式中是不含括號的
2.中序表達式轉前后綴表達式
2.1中綴表達式轉后綴表達式
其實轉換的原理就是:
從左到右依次遍歷每個數字和字符,若是數字就輸出,成為后綴表達式的一部分,若是符號,判斷與棧頂元素的優先級,是右括號或優先級不高于棧頂符號(乘除優先于加減)則棧頂元素依次出棧成為后綴表達式的一部分,再將當前符號進棧,直到最終后綴表達式輸出完畢。
幾個注意點:
- 同等級的運算符比如+,先進的+的優先級大于之后進棧的+優先級
- 一個元素進棧,必須匹配優先級比自己低的元素才能進,否則比它高的符號都彈出來作為表達式的一部分自己再進棧,比如:棧有兩個元素,棧底為+,然后*在+上邊,那么此時-進棧,由于此時的+和 *的優先級都比-高,所以兩個都彈出來變成表達式的一部分自己再進棧
- 左括號比所有括號外邊的符號優先級高,比括號里邊的所有符號都低
- 當一個右括號進棧后,與自己最近的左括號之間的所有元素彈出成為表達式的一部分,然后左右括號抵消
2.2中綴表達式轉前綴表達式:
其實和前面差不多,和轉后綴表達式有五個不同點
- 中綴表達式轉前綴表達式是自右向左掃描的
- 右括號比所有括號外邊的符號優先級高,比括號里邊的所有符號都低
- 當一個左括號進棧后,與自己最近的右括號之間的所有元素彈出成為表達式的一部分,然后左右括號抵消
- 中綴轉后綴還有一個不一樣的是,棧頂與當前遇到的新的運算符屬于同級運算符時,棧頂即要出棧,因為是同級先算左邊;然后中綴轉前綴由于是右到左遍歷的,所以同級運算符不出棧,因為出棧則代表最后的結果要先算右邊
- 得到的表達式倒置就是我們需要的前綴表達式
還拿1+((2+3)*4)-5舉例子:
3.運用棧的后綴表達式實現計算器原理步驟
3.1后綴表達式實現計算器原理
程序初始化兩個棧,一個是OPTR(運算符棧),一個是OPND(操作數棧),然后掃描表達式,一個一個的讀入字符。在把我們輸入的中綴表達式裝換成后綴表達式的同時進行計算
程序是如何邊轉換邊計算呢,比如9+(3-1)*3,我們從左到右掃描,那么OPTR和OPND兩個棧的元素變化如下
| 9 | 9 | 空 |
| + | 9 | + |
| ( | 9 | +( |
| 3 | 93 | +( |
| - | 93 | +(- |
| 1 | 931 | +(- |
| ) | 92 | + |
| * | 92 | +* |
| 3 | 923 | +* |
| 無元素可以掃描 | 96 | + |
| 無元素可以掃描 | 15(最終結果) | 空 |
可以發現只要彈出一個運算符就會在操作數棧中彈出兩個操作數,先出來的在右后出來的在左,讓彈出來的運算符對兩個操作數進行計算,計算完畢后壓入操作數棧中
3.2后綴表達式實現計算器原理的步驟
我們的程序是不會知道你輸入的表達式是否開始和結束,那么此時我們使用#來表示輸入開始和結束,比如我們想計算2+2,那么就需要輸入2+2#(我們可以在初始化的時候把起始的#壓入運算符棧)然后回車,讓我們的程序計算。計算的過程是:程序初始化兩個棧,一個是OPTR(運算符棧),一個是OPND(操作數棧),然后掃描表達式,一個一個的讀入字符(我們把數字和操作符都看成字符),,如果表達式沒有掃描完畢(即沒有遇到#)或者說OPTR的棧頂元素不為#時則循環執行下面的操作:
1.若字符不是運算符,則壓入OPND棧中,讀入下一個字符
2.若字符是運算符則根據OPTR的棧頂元素和新掃描的字符的優先級比較結果,做不同的處理
? ?(1)若是小于,則ch壓入OPTR棧,再讀入下一個字符
? ?(2)若是大于,則彈出OPTR棧頂的運算符,從OPND棧彈出兩個數,進行相應運算,結果壓入OPND棧
? ?(3)若是等于,則OPTR的棧頂元素是“(”且新掃描的字符為“)”,這時彈出OPTR棧頂的“(”相當于括號匹配成功,然后讀入下一個字符
4. 代碼實現和分析
#include<stdio.h> const char oper[7] = { '+', '-', '*', '/', '(', ')', '#' }; #define OK 1 #define ERROR 0 #define OVERFLOW -2 typedef char SElemType; typedef int Status; typedef struct SNode {int data;struct SNode *next; } SNode, *LinkStack;//構造一個空棧 Status InitStack(LinkStack &S) {S = NULL;return OK; }//判斷是否為空棧 bool StackEmpty(LinkStack S) {if (!S)return true;return false; }//用e返回S的項元素 Status GetTop(LinkStack &S) {if (!S)return ERROR;return S->data; }//插入e為新的項元素 Status Push(LinkStack &S, SElemType e) {SNode *p = new SNode;if (!p) {return OVERFLOW;}p->data = e;p->next = S;S = p;return OK; }//刪除S的項元素,并用e返回其值 Status Pop(LinkStack &S, SElemType &e) {SNode *p;if (!S)return ERROR;e = S->data;p = S;S = S->next;delete p;return OK; }/*判斷輸入的某個字符是否是運算符*ch表示輸入的字符*oper數組中存放系統能識別的運算符*/ bool In(char ch) {for (int i = 0; i < 7; i++) {if (ch == oper[i]) {return true;}}return false; }/*比較兩個運算符的優先級*a,b中存放待比較的運算符*/ char Precede(char a, char b) {if ((a == '(' && b == ')') || (a == '#' && b == '#')) {return '=';} else if (a == '(' || a == '#' || b == '(' || (a == '+' || a == '-') && (b == '*' || b == '/')) {return '<';} elsereturn '>'; }/*進行兩數的運算*a,b中分別以char型存放兩個待運算的操作數*theta中存放代表操作符的字符*結果以char型返回*/ char Operate(char a, char theta, char b) {switch (theta) {case '+':return (a - '0') + (b - '0') + 48;case '-':return (a - '0') - (b - '0') + 48;case '*':return (a - '0') * (b - '0') + 48;case '/':return (a - '0') / (b - '0') + 48;}return 0; }//算法3.22 表達式求值 char EvaluateExpression() {//算術表達式求值的算符優先算法,設OPTR和OPND分別為運算符棧和操作數棧LinkStack OPTR, OPND;char ch, theta, a, b, x, top;InitStack(OPND); //初始化OPND操作數棧InitStack(OPTR); //初始化OPTR運算符棧Push(OPTR, '#'); //將表達式起始符“#”壓入OPTR棧scanf("%c",&ch);while (ch != '#' || (GetTop(OPTR) != '#')) //表達式沒有掃描完畢或OPTR的棧頂元素不為“#”{if (!In(ch)) {Push(OPND, ch);scanf("%c",&ch);} //ch不是運算符則進OPND棧elseswitch (Precede(GetTop(OPTR), ch)) //比較OPTR的棧頂元素和ch的優先級{case '<': //棧頂元素優先級低Push(OPTR, ch);scanf("%c",&ch); //當前字符ch壓入OPTR棧,讀入下一字符chbreak;case '>':Pop(OPTR, theta); //彈出OPTR棧頂的運算符Pop(OPND, b);Pop(OPND, a); //彈出OPND棧頂的兩個運算數Push(OPND, Operate(a, theta, b)); //將運算結果壓入OPND棧break;case '=': //OPTR的棧頂元素是“(”且ch是“)”Pop(OPTR, x);scanf("%c",&ch); //彈出OPTR棧頂的“(”,讀入下一字符chbreak;} //switch} //whilereturn GetTop(OPND); //OPND棧頂元素即為表達式求值結果 }int menu() {int c;printf("0-9以內的多項式計算\n" );printf("1.計算\n");printf("0.退出\n");printf("選擇:");scanf("%d",&c);return c; }int main() {do{switch (menu()) {case 1: {printf("請輸入要計算的表達式(操作數和結果都在0-9的范圍內,以#結束):\n如 2+2# \n" );char res = EvaluateExpression();//算法3.22 表達式求值printf("計算結果為%d\n",res - 48);printf("--------------------------------------\n");}break;case 0:printf("退出成功\n");return 0;default:break;}}while (1);return 0; }例子:
總結
以上是生活随笔為你收集整理的前、中、后缀表达式概述及转换+栈的计算器原理及代码分析(含完整源码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用栈实现进制的转换
- 下一篇: 用栈实现括号匹配的检验