实验三---四则运算表达式求值
一、基本要求:
(
1 ) 利用二叉树后序遍历来实现表达式的转换,同时可以使用实验三的结果来求解后缀表达式的值。
(
2) 输入输出格式:
输入格式:在字符界面上输入一个中缀表达式,回车表示结束。
请输入表达式:
输入一个中缀表达式
输出格式:如果该中缀表达式正确,那么在字符界面上输出其后缀表达式,其中后
缀表达式中两相邻操作数之间利用空格隔开;如果不正确,在字符界面上输出表达式错误提示。
逆波兰表达式为:
输出逆波兰表达式
运算结果为:输出运算后的结果
测试数据
输入: 21+23*( 12-6)
输出: 21 23 12 6 -*+
二、基本思路
中缀表达式-》二叉树-》后序遍历-》计算
1.中缀表达式-》二叉树の需要考虑的问题:
- 保存的二叉树是什么样子的,才能使得后序遍历就能得到后缀表达式
操作符的等级设置
最上面的等级最小
设计函数ToTree来实现此过程:该过程递归
递归的判断:
1.怎样按同类更小问题解决:找到当前字符串中的最小等级操作符给data,左边的表达式给左子树,右边的表达式给右子树,左边和右边分别是一个更短的字符串的分支问题
2.各个递归怎么减小问题规模:左边和右边分别是一个更短的字符串的分支问题
3.什么情况可以当做基例:到叶子节点的情况,也就是说表达式里面没有运算符了,然后data=操作数,左右子树=NULL
4.能否最后达到基例:可以要么我写个P啊
- 怎么找到当前字符串中的最小等级的操作符?
首先先要把各个字符都赋好等级,我把数字也赋值了,最开始没有,还写了个函数提取了所有操作符,后来发现这样很难把等级数组和算式相对应,又推倒重新写了一次,我心在滴血!,如果把数字都负上就可以对上了,查找最小的时候下标是可以返回的,对应了相应的操作符。
for(i=0;suanshi[i]!='\0';i++) { if(suanshi[i]=='(') { duoshaoji++;//记录第几层括号,几层括号就把里面的运算符的等级提高n*100<img alt="惊讶" src="http://static.blog.csdn.net/xheditor/xheditor_emot/default/ohmy.gif" />?它好聪明! dengji[i]=0; dengji[i]=0; } else if(suanshi[i]==')') { duoshaoji--; dengji[i]=0; } else if(suanshi[i]=='+'||suanshi[i]=='-') dengji[i]=1+duoshaoji*100; else if(suanshi[i]=='*'||suanshi[i]=='/') dengji[i]=2+duoshaoji*100; else dengji[i]=-1; }
- 怎么处理()的问题
因为在树中我们不需要(),所以()肯定不能到树里,但是我们还需要()来辅助判断运算等级,经过分析发现,只要把最开始进函数的表达式两边的括号去掉就行了,这样就对后面的不影响了。
if(suanshi[0]=='('&&suanshi[length]==')') { suanshi++; suanshi[length-1]='\0'; }//去括号的过程,只去那种开头是括号结尾是括号的情况
- 最后的数字和前面的char型不符合,怎么办?
最开始我设的树节点是一个char,后来用了什么ASII的编码把char-》int的变来变去,还是不成,最后只好托马重写,改成了char数组的形式,这样就随之而来一些问题,在之后的后序遍历赋值里会遇到。
- 这一段是我闹心的心情札记:接着昨天的唠唠叨叨,心碎,今天被猪脚再次挑出个毛病,算起来又是我之前写的逆波兰的程序的错误,我觉得有时间的话我真的应该把逆波兰的程序在写一遍了,我不会说我上节课改逆波兰改了4次吧,算上这回应该有5、6次了吧,心碎,不过也确实怪我自己考虑问题不全,没有想到数字和操作符的数目可能不相符的情况,又让这个猪脚挑出了猫饼,以后我要叫他猫饼猪脚。这么萌的名字一定配的上他用的可爱的少女心的粉色本本~
void ToTree(BiTree &T,char *suanshi) { int duoshaoji=0,i=0,length=suanshilength(suanshi),j=0,min=0,minn=0,k=0; char *lsuanshi=NULL,*rsuanshi=NULL,exc=NULL; if(suanshi[0]=='('&&suanshi[length]==')') { suanshi++; suanshi[length-1]='\0'; }//去括号的过程,只去那种开头是括号结尾是括号的情况 length=suanshilength(suanshi); int *dengji; dengji=(int *)malloc((length+1)*sizeof(int)); for(i=0;suanshi[i]!='\0';i++) { if(suanshi[i]=='(') { duoshaoji++; dengji[i]=0; } else if(suanshi[i]==')') { duoshaoji--; dengji[i]=0; } else if(suanshi[i]=='+'||suanshi[i]=='-') dengji[i]=1+duoshaoji*100; else if(suanshi[i]=='*'||suanshi[i]=='/') dengji[i]=2+duoshaoji*100; else dengji[i]=-1; } T=(BiTreeNode *)malloc(sizeof(BiTreeNode)); if(checkfuhao(suanshi))//有符号的情况 { for(j=1;j<length+1;j++) { if(dengji[j]>0) { min=dengji[j]; minn=j; break; } } for(j=1;j<length+1;j++) { if(dengji[j]>0&&min>dengji[j]) { min=dengji[j]; minn=j; } } T->data[0]=suanshi[minn]; T->data[1]='\0'; //printf("****%c\n",T->data); lsuanshi=suanshi; lsuanshi[minn]='\0'; rsuanshi=&suanshi[minn+1]; ToTree(T->leftChild,lsuanshi); ToTree(T->rightChild,rsuanshi); } else//没有符号的情况 { strcpy(T->data,suanshi); T->leftChild=NULL; T->rightChild=NULL; } free(dengji); }
- 进行后序遍历时:要注意我们后序遍历输出的同时,主要的操作是把后序遍历的结果给到逆序的字符串里,这个要用到strcat……全都是我的鱼唇的想法,大家有好的思路可以告诉我,我觉得自己做的好蠢
void preorder(BiTree t,char *p) { int k=0,x=0; if(t) { preorder(t->leftChild,p); preorder(t->rightChild,p); if(t->data[0]=='+'||t->data[0]=='-'||t->data[0]=='/'||t->data[0]=='*') {printf("%c ",t->data[0]); if(*p!='\0') { strcat(p," "); strcat(p,t->data);} else { strcat(p,t->data);} } else {printf("%s ",t->data); if(*p!='\0') { strcat(p," "); strcat(p,t->data);} else { strcat(p,t->data);} } } }
- 最开始的时候极其鱼唇,居然没有建立树,给树空间,导致后面怎么都不对,后来看了一遍又一遍才知道自己里错了。
其他的是上次的逆波兰~
贴一下所有代码
// #include "stdafx.h" #define MAXNUM 100 #define OVERFLOW -2; #include<iostream> #include<string> #include<stdlib.h> #include<stdio.h> #include<iomanip> using namespace std; char suan[MAXNUM],*p=suan; typedef struct node { struct node *leftChild; struct node *rightChild; char data[15]; }BiTreeNode, *BiTree; int suanshilength(char *suanshi) { int i=0,k=0; for(i=0;suanshi[i]!='\0';i++) { k++; } k--; return k; } bool checkkuohao(char *suanshi) { int i=0,k=0; bool flag=1; for(i=0;suanshi[i]!='\0';i++) { if(k<0) {flag=0;break; } if(suanshi[i]=='(') k++; if(suanshi[i]==')') k--; } if(k!=0) flag=0; return flag; } bool checkfuhao(char *suanshi) { int i=0; bool flag=0; for(i=0;suanshi[i]!='\0';i++) { if(suanshi[i]=='+'||suanshi[i]=='-'||suanshi[i]=='*'||suanshi[i]=='/') { flag=1; } } return flag; } void ToTree(BiTree &T,char *suanshi) { int duoshaoji=0,i=0,length=suanshilength(suanshi),j=0,min=0,minn=0,k=0; char *lsuanshi=NULL,*rsuanshi=NULL,exc=NULL; if(suanshi[0]=='('&&suanshi[length]==')') { suanshi++; suanshi[length-1]='\0'; }//去括号的过程,只去那种开头是括号结尾是括号的情况 length=suanshilength(suanshi); int *dengji; dengji=(int *)malloc((length+1)*sizeof(int)); for(i=0;suanshi[i]!='\0';i++) { if(suanshi[i]=='(') { duoshaoji++; dengji[i]=0; } else if(suanshi[i]==')') { duoshaoji--; dengji[i]=0; } else if(suanshi[i]=='+'||suanshi[i]=='-') dengji[i]=1+duoshaoji*100; else if(suanshi[i]=='*'||suanshi[i]=='/') dengji[i]=2+duoshaoji*100; else dengji[i]=-1; } T=(BiTreeNode *)malloc(sizeof(BiTreeNode)); if(checkfuhao(suanshi))//有符号的情况 { for(j=1;j<length+1;j++) { if(dengji[j]>0) { min=dengji[j]; minn=j; break; } } for(j=1;j<length+1;j++) { if(dengji[j]>0&&min>dengji[j]) { min=dengji[j]; minn=j; } } T->data[0]=suanshi[minn]; T->data[1]='\0'; //printf("****%c\n",T->data); lsuanshi=suanshi; lsuanshi[minn]='\0'; rsuanshi=&suanshi[minn+1]; ToTree(T->leftChild,lsuanshi); ToTree(T->rightChild,rsuanshi); } else//没有符号的情况 { strcpy(T->data,suanshi); T->leftChild=NULL; T->rightChild=NULL; } free(dengji); } void preorder(BiTree t,char *p) { int k=0,x=0; if(t) { preorder(t->leftChild,p); preorder(t->rightChild,p); if(t->data[0]=='+'||t->data[0]=='-'||t->data[0]=='/'||t->data[0]=='*') {printf("%c ",t->data[0]); if(*p!='\0') { strcat(p," "); strcat(p,t->data);} else { strcat(p,t->data);} } else {printf("%s ",t->data); if(*p!='\0') { strcat(p," "); strcat(p,t->data);} else { strcat(p,t->data);} } } } typedef struct nibolanstack //建立栈类 { double *base; double *top; int stacksize; }nibolans; bool InitStack(nibolans &S){ //创建一个空栈 S.base=(double *)malloc(50*sizeof(double)); // if(!S.base) exit(OVERFLOW); S.top=S.base; S.stacksize=50; return 1; } bool push(nibolans &S,double e) //插入元素 { if(S.top-S.base>=S.stacksize) { S.base=(double *)realloc(S.base,(S.stacksize+20)*sizeof(double)); if(!S.base) exit(-2); S.top=S.base+S.stacksize; S.stacksize=S.stacksize+20; } *(S.top)=e; S.top++; return 1; } bool pop(nibolans &S,double &e) //出栈 { if(S.top==S.base) return 0; S.top--; e=*S.top; return 1; } void check(char s[],nibolans &S) { char *p=s,q[10]; int i=0,k=0; double m,x,y,z; // cout<<s<<endl; while(*p!='#') { if(*p==' ') { // cout<<endl; m=atof(q); // cout<<m<<endl; if(m!=0) push(S,m); for(int j=0;j<i;j++) { q[j]='\0';//char型置空 } i=0; } else { // cout<<*p<<" "; // cout<<(*p=='+'); if((*p=='+')||(*p=='-')||(*p=='*')||(*p=='/')) { pop(S,x); pop(S,y); if(*p=='+') { z=x+y; push(S,z); } if(*p=='-') { z=y-x; push(S,z); } if(*p=='*') { z=x*y; push(S,z); } if(*p=='/') { z=y/x; push(S,y/x); } } else { q[i]=*p; i++; } } p++; } // return z; } bool checkk(char s[]) { bool flag=0; char *p=s; char *q=s; while(*q!='\0') { if((*q=='+')||(*q=='-')||(*q=='*')||(*q=='/')) { flag=1; } q++; } while(*p!='\0') { if((*p!='0')&&(*p!='1')&&(*p!='2')&&(*p!='3')&&(*p!='4')&&(*p!='5')&&(*p!='6')&&(*p!='7')&&(*p!='8')&&(*p!='9')&&(*p!='+')&&(*p!='-')&&(*p!='*')&&(*p!='/')&&(*p!=' ')&&(*p!='#')) { flag=0; } p++; } return flag; } int main(int argc, char* argv[]) { char suanshi[MAXNUM]; double jieguo; BiTree BT; nibolans S; InitStack(S); printf("请输入中缀表达式:\n"); gets(suanshi); if(checkkuohao(suanshi)) { ToTree(BT,suanshi); preorder(BT,p); cout<<endl; strcat(suan,"#"); puts(suan); if(checkk(suan)) { check(suan,S); pop(S,jieguo); cout<<fixed<<setprecision(2)<<jieguo; } else cout<<"表达式错误!"<<endl; } else cout<<"表达式错误!"<<endl; return 0; }
接下来是我几个不太熟悉的函数总结一下:
1.复制字符串
strcpy(T->data,suanshi);
把后面的复制到前面的地方。C语言标准库函数strcpy,把从src地址开始且含有‘\0‘结束符的字符串复制到以dest开始的地址空间。
2.字符串后追加字符串
strcat(p,t->data)
把p指向的字符串追加到t-》data后面,两者都得有‘、0’