根據表達式序列(前綴、中綴、后綴)構建表達式樹


如果輸入序列是表達式(前綴表達式、中綴表達式、后綴表達式,中綴表達式要求帶括號有幾個運算符就帶幾個)則構建出來的樹為表達式樹,對該樹前、中、后序遍歷得到對應序的表達式。

不過,中綴表達式帶括號,而表達式樹不帶括號,故中序遍歷表達式樹時需要加適當的括號才能得到正確的中綴表達式。

1、表達式樹的構建與遍歷

0、工具函數(鏈表節點定義、讀取下一個字符、判斷字符是否操作數):

 1 typedef struct node
 2 {
 3     char data;
 4     struct node * lchild;
 5     struct node * rchild;
 6 }BTNode,*BTREE;
 7 
 8 
 9 char nextToken(char infix[])//括號、操作數、操作符等 都為一個字符 
10 {
11     static int pos=0;
12     while(infix[pos]!='\0' && infix[pos]==' '){pos++;}//跳過空格 
13     return infix[pos++];
14 }
15 int isOpNum(char ch)//是否是操作數 
16 {
17     if(ch=='#' || ch=='(' || ch==')' || ch=='+' || ch=='-' || ch=='*' || ch=='/' || ch==' ' || ch=='|' )
18     {
19         return 0;
20     }
21     return 1;
22 }
View Code

1、輸入前綴表達式構建表達式樹(遞歸):

 1 void createPrefix_recursive(char prefix[],BTREE &T)
 2 {//遞歸方式_由前綴表達式構建表達式樹 
 3     char x=nextToken(prefix);
 4     
 5     T=(BTREE)malloc(sizeof(BTNode));
 6     T->data=x;
 7     T->lchild=NULL;
 8     T->rchild=NULL;
 9     
10     if(!isOpNum(x))//是操作符。前綴表達式的最后一個字符一定是操作數,所以下面的遞歸會停止。 
11     { 
12         createPrefix_recursive(prefix,T->lchild);
13         createPrefix_recursive(prefix,T->rchild);
14     }
15 }
View Code

2、輸入中綴表達式構建表達式樹(遞歸):

(要求輸入的中綴表達式帶括號,有n個運算符就有n個括號,為什么?因為加括號就是為了給運算符定優先級。進一步的,若給不帶括號的中綴表達式加括號有幾種?卡特蘭數種,因為n個運算符入棧有卡特蘭數種出棧順序,相關題:LeetCode241

 1 void createInfix_recursive(char infix[],BTREE &T)
 2 {//遞歸方式_由中綴表達式構建表達式樹,要求輸入的中綴表達式加括號,有幾個操作數就幾個括號 。如果輸入的前、中、后綴表達式都帶括號,則很容易由此法改造得到構建表達式樹的算法。 
 3     char x=nextToken(infix);
 4     
 5     T=(BTREE)malloc(sizeof(BTNode));
 6     T->lchild=NULL;
 7     T->rchild=NULL;
 8     
 9     if(x=='(')
10     {//處理括號里的表達式 
11         createInfix_recursive(infix,T->lchild);//表達式的左操作數 
12         
13         x=nextToken(infix);//表達式的操作符 
14         T->data=x;
15         
16         createInfix_recursive(infix,T->rchild);//表達式的右操作數 
17         nextToken(infix);//右括號 
18     }
19     else
20     {
21         T->data=x;
22     }
23 }
View Code

3、輸入后綴表達式構建表達式樹(非遞歸)

 1 #define M 100
 2 void createPostfix_nonrecursive(char postfix[],BTREE &T)
 3 {//非遞歸方式_由后綴表達式構建表達式樹 
 4     BTREE stack[M],p;
 5     int top=-1;
 6     char x;
 7     while(1)
 8     {
 9         x=nextToken(postfix);
10         if(x=='\0') return;
11         
12         p=(BTREE)malloc(sizeof(BTNode)) ;
13         p->data=x;
14         p->lchild=NULL;
15         p->rchild=NULL;
16         
17         if(isOpNum(x))
18         {//操作數 
19             stack[++top]=p;
20         }
21         else
22         {//操作符 
23             p->lchild=stack[top-1];
24             p->rchild=stack[top];
25             stack[top-1]=p;
26             top--;
27             T=p;
28         }
29     }
30     T=stack[0];
31 }
View Code

4、表達式樹遍歷(遞歸,前綴、中綴、后綴):(中綴遍歷在遍歷時需要加括號才能得到正確結果,方法為訪問到內部節點時加左右括號。)

 1 void searchPrefix(BTREE T)
 2 {
 3     if(T!=NULL)
 4     {//·ÃÎʵ½ÄÚ²¿½Úµãʱ×óÓÒ¼ÓÀ¨ºÅ 
 5 //        if(T->lchild!=NULL && T->rchild!=NULL){   printf("(");   }
 6 
 7         printf("%c",T->data);
 8         searchPrefix(T->lchild);
 9         searchPrefix(T->rchild);
10         
11 //        if(T->lchild!=NULL && T->rchild!=NULL){  printf(")");  }
12     }
13 }
14 void searchInfix(BTREE T)
15 {
16     if(T!=NULL)
17     {//ÖÐÐò±éÀú±í´ïʽÊ÷ÐèÒªÌí¼ÓÀ¨ºÅÐÅÏ¢£¬ÒÔ±£Ö¤ÔËËãÓÅÏȼ¶£º·ÃÎʵ½ÄÚ²¿½Úµãʱ×óÓÒ¼ÓÀ¨ºÅ 
18         if(T->lchild!=NULL && T->rchild!=NULL){   printf("(");   }
19         
20         searchInfix(T->lchild);
21         printf("%c",T->data);
22         searchInfix(T->rchild);
23         
24         if(T->lchild!=NULL && T->rchild!=NULL){  printf(")");  }
25     }
26 }
27 void searchPostfix(BTREE T)
28 {
29     if(T!=NULL)
30     {//·ÃÎʵ½ÄÚ²¿½Úµãʱ×óÓÒ¼ÓÀ¨ºÅ 
31 //        if(T->lchild!=NULL && T->rchild!=NULL){   printf("(");   }
32         
33         searchPostfix(T->lchild);
34         searchPostfix(T->rchild);
35         printf("%c",T->data);
36         
37 //        if(T->lchild!=NULL && T->rchild!=NULL){  printf(")");  }
38     }
39 }
View Code

如果遍歷前綴、后綴表達式時也要加括號,則方法一樣,在訪問到內部節點時加左右括號即可。從這里我們受到啟發,不借助程序,手動將中綴表達式轉換為前后綴表達式的方法:把括號內的操作符放到括號前面或后面然后去掉括號即可;反之,要將前后綴表達式轉為中綴表達式,只要加上括號然后把操作符放到括號內兩操作數中間即可!

5、測試:

 1 int main()
 2 { 
 3 //    *+A/-BCDE
 4 //    ((A+((B-C)/D))*E)
 5 //    ABC-D/+E*
 6     char str[]="ABC-D/+E*";
 7     BTREE T;
 8     
 9 //    createPrefix_recursive(str,T);
10 //    createInfix_recursive(str,T);
11     createPostfix_nonrecursive(str,T);
12 
13     
14     searchPrefix(T);      
15     printf("\n");
16     
17     searchInfix(T);
18     printf("\n");
19     
20     searchPostfix(T);
21     printf("\n");
22     
23     return 0;
24 }
View Code

2、前中后綴表達式間的轉換(以中綴表達式轉后綴表達式為例)

// *+A/-BCDE
// ((A+((B-C)/D))*E)
// ABC-D/+E*

2.1、轉換方法

手動轉換:把括號內的操作符放到括號前面或后面然后去掉括號即可;反之,要將前后綴表達式轉為中綴表達式,只要加上括號然后把操作符放到括號內兩操作數中間即可!

程序轉換:

法1:由中綴表達式構建表達式樹,然后后序遍歷該表達式樹即可

法2:不借助表達式樹,通過定義操作符的優先級純借助棧來實現,如下:

 1 #include<stdio.h>
 2 char nextToken(char infix[])//括號、操作數、操作符等 都為一個字符 
 3 {
 4     static int pos=0;
 5     while(infix[pos]!='\0' && infix[pos]==' '){pos++;}//跳過空格 
 6     return infix[pos++];
 7 }
 8 int isOpNum(char ch)//是否是操作數 
 9 {
10     if(ch=='#' || ch=='(' || ch==')' || ch=='+' || ch=='-' || ch=='*' || ch=='/' || ch==' ')
11     {
12         return 0;
13     }
14     return 1;
15 }
16 int isCurchPriorToChtop(char chCur,char chTop)//當前元素是否比棧頂元素優先級大 
17 {
18     if(chCur=='(' || chTop=='#' || chTop=='(') return 1;
19     else if( (chCur=='*'||chCur=='/') && chTop!='*' && chTop!='/' ) return 1;
20     else return 0;
21 }
22 
23 //輸入中綴表達式轉后綴,單純用堆棧。假定中綴表達式符合語法。操作數或操作符都為一個char,可能有空格  
24 //也可通過對輸入的中綴表達式構建表達式樹然后后序遍歷來得到后綴表達式。 
25 #define M 100
26 void infix2Postfix(char infix[])
27 {
28     char stack[M],x;
29     int top=-1;
30     stack[++top]='#';
31     while(1)
32     {
33         x=nextToken(infix);
34         if(isOpNum(x))
35         {
36             printf("%c",x);
37         }
38         else if(x=='#')
39         {//中綴表達式掃描結束 
40             while(top>=0)
41             {
42                 printf("%c",stack[top--]);
43             }
44             return;
45         }
46         else if(x==')')
47         {
48             while(stack[top]!='(')
49             {
50                 printf("%c",stack[top--]);
51             }
52             top--;
53         }
54         else
55         {
56             while(!isCurchPriorToChtop(x,stack[top]))
57             {
58                 printf("%c",stack[top--]);
59             }
60             stack[++top]=x;
61         }
62     }
63 }
64 int main()
65 {
66     char infix[]="(((A+(B-C)/D) *E)) #";
67     int n=sizeof(infix)/sizeof(char);
68     infix2Postfix(infix);
69     return 0;
70 }
View Code

2.2、轉換相關的性質

中綴表達式轉前后綴表達式后,操作數順序不變、操作符順序會改變,但前后綴表達式的操作符順序恰好相反

2.3、前后綴表達式的計算

前綴表達式:從后往前掃,遇到操作數入棧、遇到字符時取兩棧頂元素進行相應運算后結果入棧。

后綴表達式:與上類似,只是是從前往后掃。

 

相關閱讀:二叉樹-MarchOn

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM