算数表达式求值

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

/***     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,如果使用这个函数的话那这一功能的代码将大大简化,可惜我将对算术表达式正确性的检查也写在这一功能里面了,显得比较乱,所以也就没有更新这一功能了。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值