算数表达式求值
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,如果使用这个函数的话那这一功能的代码将大大简化,可惜我将对算术表达式正确性的检查也写在这一功能里面了,显得比较乱,所以也就没有更新这一功能了。
/*** 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,如果使用这个函数的话那这一功能的代码将大大简化,可惜我将对算术表达式正确性的检查也写在这一功能里面了,显得比较乱,所以也就没有更新这一功能了。
相关文章推荐
- 2016 UESTC Training for Data Structures N - 秋实大哥搞算数 CDOJ 1074 栈 表达式求值
- Dijkstra的双栈算数表达式求值算法
- QT算数表达式求值
- 算法(第四版) Dijkstra 算数表达式求值算法-双栈
- Java字符串算数表达式求值
- 栈3-算数表达式求值
- 算数表达式求值
- Dijkstra 的双栈算数表达式求值算法的学习
- 2015 UESTC 数据结构专题N题 秋实大哥搞算数 表达式求值/栈
- 算数表达式求值C++实现
- 简单算数表达式求值
- C语言 算数表达式求值(顺序栈应用实例)
- 算数表达式求值
- 算数表达式求值
- 算数表达式求值(C语言)
- 运用栈对算数表达式求值
- 算数表达式求值(中缀表达式转后缀表达式并求值)
- C++ 实现 算数表达式求值
- CDOJ 1074 秋实大哥搞算数(栈_表达式求值)
- 算数表达式求值