您的位置:首页 > 其它

算数表达式求值

2007-06-08 17:23 405 查看
通过表达式二叉树进行算术表达式求值,输入一个算术表达式,其中操作数必须为实数,运算符包括加、减、乘、除、小(圆)括号,还可以将此算术表达式所对应的表达式二叉树以文件的形式保存,同时能从文件中读入保存的表达式二叉树,同时还能够检查出绝大部分的语法错误。

/*** headfile.h ***/


#include<cstring>


#include<fstream.h>




#include "ToExpTree.h"

/*** ToExpTree.h ***/






#ifndef TOEXPTREE_H


#define TOEXPTREE_H


//#include<iostream>


#include<fstream>


#include<cstring>


#define MAX 30




struct tree_node......{


bool fc; //true is float,false is char




union ......{


float number; //数


char ch; //操作符


};


struct tree_node *left_child,*right_child;


};




class ToExpTree......{


public:


ToExpTree(); //缺省构造函数


tree_node * CreatTree(void); //后缀表达式建表达式二叉树


void TravPre(tree_node *&head); //先序遍历二叉树


void TravIn(tree_node *&head); //中序遍历二叉树


void TravPost(tree_node *&head); //后序遍历二叉树


bool divideop(char (&ach)[100]);


//将操作数和操作符从数组中提取出来,将其地址依次保存pnode_in中


//返回值为true时说明该表达式没错,为false时则有误


void InToPostfix(void);//中缀表达式转为后缀表达式




void display(tree_node *(&pnode)[MAX]); /**//**//**/////////验证函数


void GiveHead(tree_node *); //给tree_head 赋值


tree_node *get(void); //提供tree-head的外部接口


float GetResult(tree_node *&head); //根据二叉树计算表达的值


bool Save(void); // 保存


bool Read(void); //读文件


private:


tree_node *pnode_in[MAX],*pnode_post[MAX],*pnode_tree[MAX],*tree_head;


//中缀栈,后缀栈,建树时用的栈,树的头结点


int pnode_in_num; //做pnode的下标,及记录pnode的存储个数


ofstream outfile;


ifstream infile;


char gettop(char (&ach)[100],int &m);//取栈顶元素


tree_node *& gettop(tree_node *(&pnode)[MAX],int &m); //取栈顶元素


bool push(tree_node *(&pnode)[MAX],tree_node *&a_node,int &m);//入栈


bool push(char (&ach)[100],char ch,int &m); //入栈,重载


tree_node * pop(tree_node *(&pnode)[MAX],int &m); //出栈


char pop(char (&ach)[100],int &m);//出栈,重载


bool CmpOp(char a,char b);//比较操作符的优先级


bool ToFloat(char **ct,float &ft,int &ht);


//将字符形式的数转换为float型数,若表达式有错误,返回false


void visit(tree_node *&head); //输出结点


void savetree(tree_node *&head);//保存二叉树


void readtree(tree_node *&head);//读出二叉树的结构


};


#endif

/**** ToExpTree.cpp ****/




//将操作数和操作符从数组中提取出来,将其地址依次保存pnode中


//返回值为true时说明该表达式没错,为false时则有误






/**//*#include<iostream>


#include<fstream>


#include"ToExpTree.h"


using std::cout;


using std::endl;


using std::cin;


using std::cerr;*/


#include"headfile.h"


ToExpTree::ToExpTree()




...{


int i;




for(i=0;i<MAX;i++)...{


pnode_in[i]=NULL;


pnode_post[i]=NULL;


pnode_tree[i]=NULL;


}


}


//void ToExpTree::ToExpTree()


//将字符形式的数转换为float型数,若表达式有错误,返回false


bool ToExpTree::ToFloat(char **ct,float &ft,int &ht)




...{


char *pt=*ct;


int zh=0,negative=1;


//zh用来暂存整数部分,ft暂存小数部分,n表示小数点后的位数,


//negative=1时表示正数,-1时表示负数


float fk;


ft=0; //ft复位




if(*pt=='-'||*pt=='+')...{


if(*pt=='-')negative=-1; //为负数


pt++;ht++;


}




while(*pt>='0'&&*pt<='9')...{ //计算整数部分


zh=zh*10+(*pt-'0');


ht++;pt++;


}




if(*pt!='.')...{ft=static_cast<float>(zh)*negative;*ct=pt;return true;} //当不是小数点时退出函数


pt++;


ht++;




for(fk=1;*pt>='0'&&*pt<='9';pt++,ht++)...{


fk*=0.1;


ft=ft+(*pt-'0')*fk;


}




if(*pt=='.')...{*ct=pt;return false;} //有多个小数点的错误


ft=(zh+ft)*negative;


*ct=pt;


return true;


}




bool ToExpTree::divideop(char (&abc)[100])...{


tree_node *p;


char *cp,*cq;


int n=100;


bool qian=false;


//true表示前面为数或')',false则表示其他,用于检测表达式的正误.初始化为false


float operand;int hi=0;//hi计算数组的下标


int count=0; //用来计算括号是否匹配


pnode_in_num=0;//初始化pnode_in 的下标


cp=abc;




while(*cp!=''&&hi<=n)...{




if(*cp>='0'&&*cp<='9'||*cp=='-'||*cp=='+')...{




if((*cp=='-'||*cp=='+')&&hi!=0)...{


cq=cp-1;


if(*cq=='(');


else goto next;


//如果'+''-'表示的不是正负号时则跳出


}


if(!ToFloat(&cp,operand,hi))return false;//表达式中数的表达有错误


p=new tree_node;


p->fc=true;


p->number=operand;


qian=true;


}




else if(*cp=='+'||*cp=='-'||*cp=='*'||*cp=='/'||*cp=='('||*cp==')')...{


next: if(qian==false&&*cp!='('&&*cp!=')')return false;//检测诸如a**b的表达式错误


qian=false;


if(*cp!='('&&hi==0)return false; //当第一个不是数字或'('时,表达式错误




if(*cp=='(')...{


if(hi!=0&&*(cp-1)>'0'&&*(cp-1)<'9')return false;


count++;


}




else if(*cp==')')...{


if(count==0||*(cp+1)!=''&&*(cp+1)>='0'&&*(cp+1)<='9')return false;


//右括号在前错误以及右括号后面直接跟操作数的错误


count--; //在最后若count不为0,则表达式错误,括号不匹配


qian=true;


}


p=new tree_node;


p->fc=false;


p->ch=*cp;


cp++;hi++;


}


else return false; //表达式中出现其他无关字符的 错误


pnode_in[pnode_in_num++]=p;


if(pnode_in_num>MAX+1)return false; //存储空间不足错误


}


cp--;


if(*cp=='+'||*cp=='-'||*cp=='*'||*cp=='/'||count!=0)


return false;//最后一个字符为操作符的错误或括号不匹配的错误


return true;


}




void ToExpTree::InToPostfix(void) //中缀转后缀




...{


char rch,top;


tree_node *p,*pnode_while[MAX];


int an=0,pn_trav=0,pn_sav=0; //分别做pnode_while下标,pnode_in的遍历及存储下标


for(int i=0;i<MAX;i++)pnode_while[i]=NULL; //初始化数组pnode_while




while(pn_trav<pnode_in_num)...{


p=pnode_in[pn_trav++];




if(p->fc)...{push(pnode_post,p,pn_sav);continue;} //为数时直接入栈


rch=p->ch;




if(!an||rch=='(')...{push(pnode_while,p,an);continue;} //当为第一个操作符或是左括号时直接入栈


top=(gettop(pnode_while,an))->ch;




if(top=='('&&rch!=')')...{push(pnode_while,p,an);continue;}




if (!CmpOp(top,rch)) ...{push(pnode_while,p,an);continue;}//栈顶操作符的优先级比即将入栈的要低




while(1)...{ //栈顶的优先级比即将入栈的要高或相等




if(rch==')'&&top=='(')...{


pop(pnode_while,an);




break;} /**//////////////////////////////////////////////////////////////////////


push(pnode_post,pnode_while[an-1],pn_sav);


pop(pnode_while,an);




if(!an)...{push(pnode_while,p,an);break;}


top=(gettop(pnode_while,an))->ch;




if(top=='('&&rch!=')')...{push(pnode_while,p,an);break;}




if(!CmpOp(top,rch))...{push(pnode_while,p,an);break;}


}


}




while(an>0)...{


p=pop(pnode_while,an);


push(pnode_post,p,pn_sav);


}


cout<<endl;


}




bool ToExpTree::push(tree_node *(&pnode)[MAX],tree_node *&a_node,int &m)...{


if(m<0||m>MAX)return false;


pnode[m++]=a_node;


return true;


}




bool ToExpTree::push(char (&ach)[100],char ch,int &m)...{


if(m<0||m>100)return false;


ach[m++]=ch;


return true;


}




tree_node * ToExpTree::pop(tree_node *(&pnode)[MAX],int &m)...{


if(m<0||m>MAX)return NULL;


return pnode[--m];


}




char ToExpTree::pop(char (&ach)[100],int &m)...{


if(m<0||m>100)return 0;


return ach[--m];


}




tree_node *& ToExpTree::gettop(tree_node *(&pnode)[MAX],int &m)...{


//if(m<0||m>MAX)return NULL;


return pnode[m-1];


}




char ToExpTree::gettop(char (&ach)[100],int &m)...{


if(m<0||m>100)return 0;


return ach[m-1];


}




bool ToExpTree::CmpOp(char a,char b)...{


//操作符(+-*/)的优先级比较函数,返回true,前者比后着大或者相等


if(a==')')return false; //右括号的优先级最低


if(b=='(')return false; //左括号的优先级最高


if(a=='+'||a=='-')


if(b=='*'||b=='/')return false;


return true;


}


tree_node * ToExpTree::CreatTree(void) //后缀表达式建表达式二叉树




...{


tree_node *left,*right,*head,*htr;


int i=0,j=0;




while((htr=pnode_post[i++])!=NULL)...{




if(htr->fc)...{


push(pnode_tree,htr,j);


htr->left_child=NULL;


htr->right_child=NULL;


continue;


}


right=pop(pnode_tree,j);


left=pop(pnode_tree,j);


head=htr;


head->left_child=left;


head->right_child=right;


push(pnode_tree,head,j);


}


return head;


}


void ToExpTree::TravPre(tree_node *&head) //先序遍历二叉树




...{




if(head) ...{


visit(head);


TravPre(head->left_child);


TravPre(head->right_child);}


return;


}


void ToExpTree::TravIn(tree_node *&head)




...{




if(head)...{


if(head->fc) visit(head);




else...{




if(!(head->left_child->fc)&&!CmpOp(head->left_child->ch,head->ch))...{


//左子树的优先级比头结点的优先级要低时


cout<<'(';


TravIn(head->left_child);


cout<<')';


}


else TravIn(head->left_child);


visit(head);




if(!(head->right_child->fc)&&!CmpOp(head->right_child->ch,head->ch))...{


//右子树的优先级比头结点的优先级要低时


cout<<'(';


TravIn(head->right_child);


cout<<')';


}


else TravIn(head->right_child);


}


}


}


void ToExpTree::TravPost(tree_node *&head)




...{




if(head)...{


TravPost(head->left_child);


TravPost(head->right_child);


visit(head);


}


return;


}


void ToExpTree::visit(tree_node *&head)




...{


if(head->fc)cout<<head->number<<" ";


else cout<<head->ch<<" ";


return;


}


void ToExpTree::display(tree_node *(&pnode)[MAX])




...{


int i;


for(i=0;i<MAX&&pnode[i]!=NULL;


i++)


visit(pnode[i]);


return;


}


void ToExpTree::GiveHead(tree_node *p)




...{


tree_head=p;


return;


}


tree_node * ToExpTree:: get(void)




...{


return tree_head;


}


float ToExpTree::GetResult(tree_node *&head) //根据二叉树计算表达式的值




...{


float result=0,Lre=0,Rre=0,swp=0;




if(!(head->fc))...{


if(head->left_child->fc) Lre=head->left_child->number;




else ...{swp=Rre;Lre=GetResult(head->left_child);Rre=swp;}


if(head->right_child->fc)Rre=head->right_child->number;




else ...{ swp=Lre; Rre=GetResult(head->right_child); Lre=swp;}




if(1)...{




switch(head->ch)...{


case '*': result=Lre*Rre;break;


case '/': result=Lre/Rre;break;


case '+': result=Lre+Rre;break;


case '-': result=Lre-Rre;break;


}


}


}


return result;


}


bool ToExpTree::Save(void) // 保存二叉树




...{


char filename[100];


cout<<endl<<"Please input the filename to save:";


cin>>filename;


outfile.open(filename);




if(!outfile)...{


cerr<<"Cannot open the file ""<<filename<<""for input,it maybe doesn't exist"<<endl;


return false;


}


savetree(tree_head);


outfile.close();


return true;


}




void ToExpTree::savetree(tree_node *&head)...{




if(head)...{


if(head->fc)outfile<<" "<<'T'<<" "<<head->number<<endl; //True


else outfile<<" "<<'F'<<" "<<head->ch<<endl; //False


if(!head->left_child)outfile<<" # "<<endl; //当其左子树为空时输出#


else savetree(head->left_child);


if(!head->right_child)outfile<<" # "<<endl; //当其右子树为空时输出#


else savetree(head->right_child);


}


return;


}


bool ToExpTree::Read(void)




...{


char filename[100];


cout<<endl<<"Please input the filename to read:";


cin>>filename;


infile.open(filename);




if(!infile)...{


cerr<<"Cannot open the file ""<<filename<<""for read,it maybe doesn't exist"<<endl;


return false;


}


readtree(tree_head);


infile.close();


return true;


}




void ToExpTree::readtree(tree_node *&head)...{


char cht;


infile>>cht;




if(cht=='#')...{head=NULL;return;}


head=new tree_node;




if(cht=='T')...{


head->fc=true;


infile>>head->number;


}




else if(cht=='F')...{


head->fc=false;


infile>>head->ch;


}


readtree(head->left_child);


readtree(head->right_child);


return;


}



/****** main.cpp *****/


#include"headfile.h"


int main(void)




......{


char s[100];int seh;


char ar;


tree_node *p;


float ff;


ToExpTree *ah=new ToExpTree;


start: cout<<"Do you want to input directly or load a saved file:"<<endl;


cout<<"1:input directly;"<<endl;


cout<<"2:load a saved file;"<<endl;


cout<<"3:Exit;"<<endl<<"please select:";


cin>>seh;




if(seh==3)......{system("pause");return 0;}




else if(seh==1)......{


cout<<"intput a expression:";


cin>>s;cout<<endl;




if(!ah->divideop(s))......{cout<<"The expression is error!!!"<<endl;goto start;}


ah->InToPostfix();


p=ah->CreatTree();


ah->GiveHead(p);


p=ah->get();


}


else if(seh==2)




......{if(!ah->Read()) goto start;}




else ......{cout<<"input error!"<<endl;goto start;}


p=ah->get();


cout<<" 前序遍历:";


ah->TravPre(p);


cout<<endl<<"中序遍历:";


ah->TravIn(p);


cout<<endl<<"后序遍历:";


ah->TravPost(p);


ff=ah->GetResult(p);


cout<<endl<<"计算结果:"<<ff;


cout<<endl<<"是否保存二叉树?(Y/N):";


cin>>ar;


if(ar=='y'||ar=='Y')


if(ah->Save())cout<<endl<<"The ExpTree had been save correctly!";


return 0;


}



正好文档还在电脑里面,这里也就顺便把其中的主要算法也贴出来吧:

(1)字符数组转换成表达式(由函数divideop完成):从数组中读入一个字符,若这个字符是数字字符的话,则进入ToFloat函数,直到下一个字符是操作符的时候才退出ToFloat函数,此时该函数返回一个float型数,得到一个操作数,然后再根据tree_node的规则完成fc的值,入栈(pnode_in)。如果读入的字符是“+”、“-”则首先应该判断这两个符号是正负号还是操作符的加减号,如果是正负号,则同样进入ToFloat,返回一个操作数,入栈(pnode_in);如果是加减号的话,则直接完成fc的赋值,再入栈。当读到“(”、“)”、“*”、“/”的时候跟加减号一样,直接入栈(pnode_in)。若是其他字符则直接退出divideop,返回false,说明该表达式有错误。同时在函数的适当的地方都加了一些检测表达式是否正确的语句,可以检测到绝大部分的表达式的错误。比如:“)”在“(”前面,两个数中间没有操作符,括号和数中间没有操作符的问题等。
(2)中缀转后缀(InToPostfix实现):此时表达式存在pnode_in中,转换后的后缀表达式将存在pnode_post中,在InToPostfix中额外定义了一个辅助堆栈(pnode_while),从pnode_in中读入。当读到一个操作数的时候立即把它入栈(pnode_post),当读到操作符时先入栈(pnode_while),当遇到“(”时也应该先入栈(pnode_while),此时从一个空栈开始计算。如果见到“)”,则将栈(pnode_while)中元素弹出,将弹出的符号入栈(pnode_post),直到遇到对应的“(”时,此时直接“(”从pnode_while中弹出而不必再入栈。如果遇到其他操作符,如果栈顶(pnode_while)元素优先级必将入栈(pnode_while)的元素的优先级高或相等,则栈顶(pnode_while)元素出栈,入栈(pnode_post),继续比较直到栈顶(pnode_while)的元素的优先级比它低为止,将该操作符入栈(pnode_while)。(“(”的优先级最高,当遇到“)”时才移走)。
(3)根据后缀表达式建立表达式二叉树(CreatTree实现):此时从pnode_post中读数,当此时用pnode_tree做辅助堆栈,当从pnode_post中读入的是操作数是直接入栈,直到遇到操作符,操作符先不入栈,依次出栈两个结点,先出来的做右操作数,后出来的做左操作数,然后将该操作符的left_child指向左操作数,right_child指向右操作数,最后将该操作符所在结点入栈。
(4)先序遍历和后序遍历:跟一般二叉树的先序和后序遍历一样,都是直接用一个递归即成。
(5)中序遍历(TravIn实现):用的也是递归,如果头结点为空,退出。如果头结点存的是操作数,则先访问头结点,退出;否则如果头结点的左子树是操作符且其优先级比头结点的要低,则先输出“(”,然后以头结点的左子树为参数进入递归,然后再输出“)”。如果左子树存的是操作数的话,直接进入递归。然后访问头结点。接下来就是处理右子树了,其方法跟处理左子树是类似的。
用这个方法就可以避免括号的冗余。
(6)保存(Save):事实上就是一个先序遍历的过程,当读到的是操作符时,先输出F(表示接下来的是一个操作符,char),然后再输出操作符。如果它的左子树为空,则输出“#”(表示为空),否则就以左子树为参数进入递归。然后就是右子树了,如果右子树为空,则输出“#”,否则就以右子树为参数进入递归。
(7)读取文件(Read):读取也是以先序来读的,当读到的字符是F时,表示接下来的是操作符;如果读到的字符是T时,表示接下来的时操作数,这样就完成了一个结点的读取。如果读到的是“#”时,表示该树为空。

参考资料
1、《数据结构(C语言版)》 严蔚敏 吴伟民 清华大学出版社
2、《数据结构与算法分析-C语言描述》(美)Mark Allen Weiss著 冯舜玺 译 机械工业出版社
3、《C++Primer》 Stanley B.Lippman著 潘爱民 译 中国电力出版社

PS:其实上面 的字符数组转换成表达式写得过于复杂了,后来无意中见过一个函数ungetc,如果使用这个函数的话那这一功能的代码将大大简化,可惜我将对算术表达式正确性的检查也写在这一功能里面了,显得比较乱,所以也就没有更新这一功能了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: