日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

表达式求值(最详细分析+代码实现+表达式之间的相互转换)

發布時間:2025/3/15 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 表达式求值(最详细分析+代码实现+表达式之间的相互转换) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

一、概念

二、前綴表達式的邏輯和實現方式

1.定義

2.前綴表達式的計算機求值

3.例子

4.代碼實現

三、中綴表達式的邏輯和實現方式

1.定義

2.中綴表達式規則

3.中綴表達式的計算機求值

4.代碼實現

四、后綴表達式的邏輯和實現方式(逆波蘭表達式求值)

1.定義

2.后綴表達式計算機求值

3.例子

4.代碼實現

五、相互轉換

1.中綴表達式轉化為前綴表達式

①算法描述

②例子

2.前綴表達式轉化為中綴表達式

3.中綴表達式轉化為后綴表達式

①算法描述

②例子

③代碼實現

4.后綴表達式轉化為中綴表達式

六、總結

1.常用表達式求值分析

①方法

②優缺點

2.相互轉換分析

3.總結


?

一、概念

算術表達式是由操作數(運算數)、運算符(操作符)、和界線符(括號)三部分組成,在計算機中進行算術表達式的計算是通過堆棧來實現的。

二、前綴表達式的邏輯和實現方式

1.定義

如果是在兩個操作數之前,那么這個表達式就是前綴表達式,又稱波蘭表達式,如:-*+3 5 7 1

2.前綴表達式的計算機求值

1.從右至左掃描表達式,遇到數字時,將數字壓入堆棧,遇到運算符時,

2.彈出棧頂的兩個數,用運算符對它們做相應的計算(棧頂元素 op 次頂元素),

3.并將結果入棧;重復上述過程直到表達式最左端,最后運算得出的值即為表達式的結果

3.例子

計算前綴表達式的值:- + 1 × + 2 3 4 5
1)從右至左掃描,將5,4,3,2壓入堆棧;
2)遇到+運算符,彈出2和3(2為棧頂元素,3為次頂元素),計算2+3的值,得到5,將5壓入棧;
3)遇到×運算符,彈出5和4,計算5×4的值,得到20,將20壓入棧;
4)遇到1,將1壓入棧;
5)遇到+運算符,彈出1和20,計算1+20的值,得到21,將21壓入棧;
6)遇到-運算符,彈出21和5,計算5-21的值,得到-16為最終結果
可以看到,用計算機計算前綴表達式是非常容易的,不像計算后綴表達式需要使用正則匹配

4.代碼實現

?

三、中綴表達式的邏輯和實現方式

1.定義

如果是跟在兩個操作數之間,那么這個表達式就是中綴表達式,如:(3 + 5) * 7 - 1

2.中綴表達式規則

(1)?先計算括號內,后計算括號外;

(2)?在無括號或同層括號內,先乘除運算,后加減運算,即乘除運算的優先級高于加減運算的優先級;

(3)?同一優先級運算,從左向右依次進行。

3.中綴表達式的計算機求值

  • 設置兩個棧,一個數字棧numStack,用于存儲表達式中涉及到的數字,operatorStack用于存儲表達式中涉及到的運算符
  • 逐個字符分析表達式,直到全部字符都已分析完
  • 若當前字符為數字,則判斷是否后續字符也為數字,若為數字則進行拼接,直到下一個數字為運算符為止,此時將拼接好的多位數字壓入數字棧中。(如果已經是最后一個字符則直接壓入棧)
  • 若當前字符為算數運算符
  • 如果運算符棧為空則直接壓入棧中
  • 運算符不為空,則對運算符優先級進行判斷
  • 如果當前運算符優先級大于等于棧頂運算符則直接壓入棧中
  • 如果優先級低于棧頂運算符,則,從數字棧中取出兩個數據,將當前棧頂運算符彈出進行運算,將結果壓入數字棧中,將當前運算符壓入運算符棧中。
  • 此時數字與運算符都已經壓入棧中,此時運算符棧中均為優先級相同的運算符,需要進行收尾操作,如果運算符棧不為空,則依次從數字棧中彈出兩個數據,與當前棧頂的運算符進行運算。將結果壓入數字棧中。最后數字棧中的數字就是所要求解的結果
  • 4.代碼實現

    #include <stdio.h> #include <string.h> #include <stdlib.h> #include <malloc.h> #include <math.h> #define maximum 100000typedef struct//數字棧 {float data[maximum];int top; }number;typedef struct//字符棧 {char data[maximum];int top; }sign;void InitNumber(number *stack);//初始化數字棧 void GetTopNumber(number stack, float *e);//獲取棧頂元素 void PushNumber(number *stack, float e);//進棧 void PopNumber(number *stack, float *e);//出棧 void InitSign(sign *stack); void GetTopSign(sign stack, char *e); void PushSign(sign *stack, char e); void PopSign(sign *stack, char *e);void Calculate(number *stack, char e);number Num; sign sig; char expression[maximum];int main() {gets(expression);int length;length=strlen(expression);int i;float en,n;char es;InitNumber(&Num);InitSign(&sig);for (i=0;i<length;i++){if(expression[i]>='0'&&expression[i]<='9'){n=expression[i]-'0';//字符型轉換為整型 while (expression[i+1]!='\0'){if (expression[i+1]>='0'&&expression[i+1]<='9') {n=n*10+expression[i+1]-'0';++i;}else break;}PushNumber(&Num,n);}else if (expression[i]=='+'||expression[i]=='-'||expression[i]=='*'||expression[i]=='/'||expression[i]=='^'||expression[i]=='('||expression[i]==')'){switch (expression[i]){case '+':if(sig.data[sig.top-1]!='+'&&sig.data[sig.top-1]!='-'&&sig.data[sig.top-1]!='*'&&sig.data[sig.top-1]!='/'&&sig.data[sig.top-1]!='^')//與棧頂元素的優先級相比較, 高于時入棧,此處判斷是否入棧。 PushSign(&sig,'+');else{while (sig.top>0&&sig.data[sig.top-1]!='(')//如果棧不為空切不為左括號,則出棧 {PopSign(&sig,&es);Calculate(&Num,es);}PushSign(&sig,'+');}break;case '-':if(sig.data[sig.top-1]!='+'&&sig.data[sig.top-1]!='-'&&sig.data[sig.top-1]!='*'&&sig.data[sig.top-1]!='/'&&sig.data[sig.top-1]!='^')PushSign(&sig,'-');else{while (sig.top>0&&sig.data[sig.top-1]!='('){PopSign(&sig,&es);Calculate(&Num,es);}PushSign(&sig,'-');}break;case '*':if(sig.data[sig.top-1]!='*'&&sig.data[sig.top-1]!='/'&&sig.data[sig.top-1]!='^')PushSign(&sig,'*');else{while (sig.top>0&&sig.data[sig.top-1]!='('){PopSign(&sig,&es);Calculate(&Num,es);}PushSign(&sig,'*');}break;case '/':if(sig.data[sig.top-1]!='*'&&sig.data[sig.top-1]!='/'&&sig.data[sig.top-1]!='^')PushSign(&sig,'/');else{while (sig.top>0&&sig.data[sig.top-1]!='('){PopSign(&sig,&es);Calculate(&Num,es);}PushSign(&sig,'/');}break;case '^':if(sig.data[sig.top-1]!='^')PushSign(&sig,'^');else{while (sig.top>0&&sig.data[sig.top-1]!='('){PopSign(&sig,&es);Calculate(&Num,es);}PushSign(&sig,'^');}case '(':PushSign(&sig,'(');break;case ')':while (sig.data[sig.top-1]!='('){PopSign(&sig,&es);Calculate(&Num,es);}PopSign(&sig,&es);}}}while (sig.top>0){PopSign(&sig,&es);Calculate(&Num,es);}GetTopNumber(Num,&en);printf("%.0f\n",en);return 0; }void InitNumber(number *stack) {stack->top=0; }void GetTopNumber(number stack, float *e) {if(stack.top==0) return;else *e=stack.data[stack.top-1]; }void PushNumber(number *stack, float e) {if(stack->top>=maximum) return;else stack->data[stack->top++]=e; }void PopNumber(number *stack, float *e) {if(stack->top==0) return;else *e=stack->data[--stack->top]; }void InitSign(sign *stack) {stack->top=0; }void GetTopSign(sign stack, char *e) {if(stack.top==0) return;else *e=stack.data[stack.top-1]; }void PushSign(sign *stack, char e) {if(stack->top>=maximum) return;//棧滿 else {stack->data[stack->top]=e;stack->top++;} }void PopSign(sign *stack, char *e) {if(stack->top==0) return;else *e=stack->data[--stack->top]; }void Calculate(number *stack, char e)// 計算結果 {float num1,num2,result;PopNumber(stack, &num2);PopNumber(stack, &num1);switch (e){case '+':result=num1+num2;PushNumber(stack,result);break;case '-':result=num1-num2;PushNumber(stack,result);break;case '*':result=num1*num2;PushNumber(stack,result);break;case '/':if (num2==0) printf("表達式錯誤!");else{result=num1/num2;PushNumber(stack,result);break;}case '^':result=pow(num1,num2);PushNumber(stack,result);break;} }

    四、后綴表達式的邏輯和實現方式(逆波蘭表達式求值)

    1.定義

    如果每個操作符跟在它的兩個操作數之后,而不是兩個操作數之間,那么這個表達式就是后綴表達,又稱為逆波蘭表達式,如:3 5 + 7 * 1 -

    2.后綴表達式計算機求值

    1.與前綴表達式類似,只是順序是從左至右:
    2.從左至右掃描表達式,遇到數字時,將數字壓入堆棧,遇到運算符時,彈出棧頂的兩個數,其中先出棧的是右操作數,后出棧的是左操作數,
    3.用運算符對它們做相應的計算(次頂元素 op 棧頂元素),并將結果入棧;
    4.重復上述過程直到表達式最右端,最后運算得出的值即為表達式的結果

    3.例子

    計算后綴表達式的值:1 2 3 + 4 × + 5 -
    1)從左至右掃描,將1,2,3壓入棧;
    2)遇到+運算符,3和2彈出,計算2+3的值,得到5,將5壓入棧;
    3)遇到4,將4壓入棧
    4)遇到×運算符,彈出4和5,計算5×4的值,得到20,將20壓入棧;
    5)遇到+運算符,彈出20和1,計算1+20的值,得到21,將21壓入棧;
    6)遇到5,將5壓入棧;
    7)遇到-運算符,彈出5和21,計算21-5的值,得到16為最終結果

    4.代碼實現

    bool isNumber(char* token) {return strlen(token) > 1 || ('0' <= token[0] && token[0] <= '9'); }int evalRPN(char** tokens, int tokensSize) {int n = tokensSize;int stk[n], top = 0;for (int i = 0; i < n; i++) {char* token = tokens[i];if (isNumber(token)) {stk[top++] = atoi(token);} else {int num2 = stk[--top];int num1 = stk[--top];switch (token[0]) {case '+':stk[top++] = num1 + num2;break;case '-':stk[top++] = num1 - num2;break;case '*':stk[top++] = num1 * num2;break;case '/':stk[top++] = num1 / num2;break;}}}return stk[top - 1]; }

    atoi函數的用法在另一篇文章有體現,其作用就是將字符串轉化為整數型。

    五、相互轉換

    1.中綴表達式轉化為前綴表達式

    ①算法描述

    (1)首先構造一個運算符棧S1和一個儲存中間結果的棧S2。
    (2)從右至左掃描中綴表達式
    (3)如果是操作數時,將其壓入S2。
    (4)如果是運算符,則與S1棧頂元素比較優先級:
    a) 如果S1為空,或棧頂運算符為右括號“)”,則直接將此運算符入棧;
    b) 否則,若該運算符優先級比棧頂運算符的較高或相等,也將運算符壓入S1;
    c) 否則,將S1棧頂的運算符彈出并壓入到S2中,再與S1中新的棧頂運算符相比較
    (5)遇到括號時:
    a) 如果是右括號“)”,則直接壓入S1;
    b) 如果是左括號“(”,則依次彈出S1棧頂的運算符,并壓入S2,直到遇到右括號為止,此時將這一對括號 丟棄;
    (6)重復步驟(2)至(5),直到表達式的最左邊;
    (7)若表達式掃描完,將S1中剩余的運算符依次出棧并壓入S2;
    (8)依次將S2中的元素出棧,結果即為對應的前綴表達式。

    ②例子

    2.前綴表達式轉化為中綴表達式

    ?

    3.中綴表達式轉化為后綴表達式

    ①算法描述

    (1)首先構造一個運算符棧S1和一個儲存中間結果的棧或線性表S2。
    (2)從左至右掃描中綴表達式
    (3)如果是操作數時,將其壓入S2。
    (4)如果是運算符,則與S1棧頂元素比較優先級:
    a) 如果S1為空,或棧頂運算符為右括號“(”,則直接將此運算符入棧;
    b) 否則,若該運算符優先級比棧頂運算符的較高或相等,也將運算符壓入S1;
    c) 否則,將S1棧頂的運算符彈出并壓入到S2中,再與S1中新的棧頂運算符相比較
    (5)遇到括號時:
    a) 如果是左括號“(”,則直接壓入S1;
    b) 如果是右括號“)”,則依次彈出S1棧頂的運算符,并壓入S2,直到遇到右括號為止,此時將這一對括號丟棄;
    (6)重復步驟(2)至(5),直到表達式的最右邊;
    (7)若表達式掃描完,將S1中剩余的運算符依次出棧并壓入S2;
    (8)從左往右依次讀取S2中的元素,即為對應的后綴表達式。

    ②例子

    ③代碼實現

    //本程序只能處理有關運算符+、-、*、/的中綴表達式,不能是÷或者×及其他運算 //界限符只能是英文狀態的左右括號即'('、')',操作數只能是整數 //本程序不會檢查輸入的中綴表達式是否正確,因此請您核驗好自己的式子是否正確 #include<stdio.h> #include<string.h> //strlen的頭文件,用于判斷字符串長度 #include<stdlib.h> //malloc、free的頭文件 #define size 50//假定要轉換的中綴表達式的字符數在50個以內 typedef struct Linknode{ //定義鏈棧及結點char data; //數據域struct Linknode *next; //指針域 }*LiStack; bool InitStack(LiStack &S){ //鏈棧的初始化,不帶頭結點S=NULL; //剛開始沒有結點return true; } bool StackEmpty(LiStack S){ //判斷棧空return S==NULL; } bool Push(LiStack &S,char x){ //將元素x入棧Linknode *s=(Linknode *)malloc(sizeof(Linknode)); //創建新結點if(s==NULL) //內存不足,創建失敗return false;s->data=x;s->next=S; //將結點s作為鏈棧的棧頂結點S=s; //棧頂指針S指向結點sreturn true; } bool Pop(LiStack &S,char &x){ //棧頂元素出棧,將值賦給xif(S==NULL)return false; //棧空則返回NULLx=S->data;Linknode *p=S;S=S->next;free(p);return true; } int main(){char temp,a[size],b[size]; //靜態數組a、b分別存放要轉換的中綴表達式和轉換后的后綴表達式,字符變量temp存放彈出的棧頂元素scanf("%s",&a); //需要您輸入中綴表達式LiStack S;//初始化一個棧,用于保存括號和暫時還不能確定運算順序的運算符InitStack(S); //初始化鏈棧int i,j,length=strlen(a); //length為輸入的中綴表達式的總長度,i、j分別為靜態數組a、b的索引下標for(i=j=0;i<length;i++){if(a[i]>=48 && a[i]<=57){ //若當前字符是數字,字符0-9的ACSII碼范圍是[48,57]b[j++]=a[i];if(a[i+1]=='+'||a[i+1]=='-'||a[i+1]=='*'||a[i+1]=='/') //若下一個字符是運算符,即+、-、*、/,則b加一個空格,以免不同的操作數混在一起b[j++]=' ';}else if(a[i]=='(')Push(S,a[i]); //若當前字符是左括號則直接入棧else if(a[i]==')'){ //若當前字符是右括號while(StackEmpty(S)==0){ //棧非空則不斷彈出棧內字符并加入后綴表達式Pop(S,temp);if(temp=='(') //直到彈出左括號停止,注意這個(不加入后綴表達式break;b[j++]=temp;b[j++]=' '; //加一個空格,從而將字符隔開}}else switch(a[i]){ //若當前字符是運算符case '*': case '/':{while(StackEmpty(S)==0){ //若棧非空,則彈出棧中優先級高于或等于當前運算符的所有運算符,并將這些運算符加入后綴表達式Pop(S,temp);if(temp=='/'||temp=='*'){b[j++]=temp;b[j++]=' '; //加一個空格,從而將字符隔開}else if(temp=='('||temp=='-'||temp=='+'){//若棧頂元素是左括號或者是優先級低于當前字符的運算符,則將棧頂元素入棧Push(S,temp);break;}}Push(S,a[i]); //把當前字符入棧break;}case '-': case '+':{while(StackEmpty(S)==0){ //若棧非空,則彈出棧中優先級高于或等于當前運算符的所有運算符,并將這些運算符加入后綴表達式Pop(S,temp);if(temp=='('){//若棧頂元素是左括號,則將棧頂元素入棧Push(S,temp);break;}else if(temp=='/'||temp=='*'||temp=='-'||temp=='+'){b[j++]=temp;b[j++]=' '; //加一個空格,從而將字符隔開}}Push(S,a[i]); //把當前字符入棧break;}}}while(StackEmpty(S)==0){ //棧非空時依次彈出棧頂元素并加入后綴表達式Pop(S,temp);b[j++]=temp;b[j++]=' '; //加一個空格,從而將字符隔開}printf("結果是:\n");for(i=0;i<j;i++) //j是數組中下一個可以插入元素的位置下標,因此b中存放字符的索引區間為[0,j-1]printf("%c",b[i]); //輸出b中的元素printf("\n");return 0; }

    4.后綴表達式轉化為中綴表達式

    同上前綴表達式轉中綴表達式的原理一致,區別在于前綴是從右往左掃描,后綴表達式是從左往右掃描。

    六、總結

    1.常用表達式求值分析

    ①方法

    常見的方法有兩種,一種是中綴表達式求值,一種是后綴表達式求值

    ②優缺點

    中綴表達式:符合人的習慣,但是在計算機中計算時要考慮其優先級和括號的關系,實現起來比較麻煩

    后綴表達式:在計算機中實現時不需要考慮優先級和括號,因為后綴表達式已經將其解決了,但是不符合人的習慣

    2.相互轉換分析

    ①關于前綴的轉換是從右向左掃描的

    ②關于后綴的轉換是從左向右轉換的

    3.總結

    不管是怎么樣轉換和怎么樣計算,思路理解起來都沒那么難,難就難在代碼的實現上,雖然我賦有代碼,但是這些代碼是我從別的博客上整理來的,由于太多了不記得那是哪了,要是涉及侵權之類的請聯系我,我立馬刪除。

    代碼有了、思路也有了,關鍵就是代碼的實現上了,這個一定要自己敲,就算是照著敲一遍也比復制粘貼的好,邊敲邊理解,到最后看能不能用自己的思路敲出來。

    作為碼農,代碼還是要多敲的,之前忽略了,現在就要惡補了,目前每天刷兩道Leetcode來彌補。

    代碼的實踐還是很重要的!

    代碼的實踐還是很重要的!

    代碼的實踐還是很重要的!

    花了一天的時間整理出來的,希望對個位有幫助,有什么不理解的也可以留言,或者加我QQ:2417734199。

    總結

    以上是生活随笔為你收集整理的表达式求值(最详细分析+代码实现+表达式之间的相互转换)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。