如果输入序列是表达式(前缀表达式、中缀表达式、后缀表达式,中缀表达式要求带括号有几个运算符就带几个)则构建出来的树为表达式树,对该树前、中、后序遍历得到对应序的表达式。
不过,中缀表达式带括号,而表达式树不带括号,故中序遍历表达式树时需要加适当的括号才能得到正确的中缀表达式。
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 }
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 }
2、输入中缀表达式构建表达式树(递归):
(要求输入的中缀表达式带括号,有n个运算符就有n个括号,为什么?因为加括号就是为了给运算符定优先级。进一步的,若给不带括号的中缀表达式加括号有几种?卡特兰数种,因为n个运算符入栈有卡特兰数种出栈顺序,相关题:)
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 else20 {21 T->data=x;22 }23 }
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 else22 { //操作符 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 }
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 }
如果遍历前缀、后缀表达式时也要加括号,则方法一样,在访问到内部节点时加左右括号即可。从这里我们受到启发,不借助程序,手动将中缀表达式转换为前后缀表达式的方法:把括号内的操作符放到括号前面或后面然后去掉括号即可;反之,要将前后缀表达式转为中缀表达式,只要加上括号然后把操作符放到括号内两操作数中间即可!
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 }
2、前中后缀表达式间的转换(以中缀表达式转后缀表达式为例)
// *+A/-BCDE
// ((A+((B-C)/D))*E)// ABC-D/+E*2.1、转换方法
手动转换:把括号内的操作符放到括号前面或后面然后去掉括号即可;反之,要将前后缀表达式转为中缀表达式,只要加上括号然后把操作符放到括号内两操作数中间即可!
程序转换:
法1:由中缀表达式构建表达式树,然后后序遍历该表达式树即可
法2:不借助表达式树,通过定义操作符的优先级纯借助栈来实现,如下:
1 #include2 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 10026 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 else55 {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 }
2.2、转换相关的性质
中缀表达式转前后缀表达式后,操作数顺序不变、操作符顺序会改变,但前后缀表达式的操作符顺序恰好相反。
2.3、前后缀表达式的计算
前缀表达式:从后往前扫,遇到操作数入栈、遇到字符时取两栈顶元素进行相应运算后结果入栈。
后缀表达式:与上类似,只是是从前往后扫。
相关阅读: