四则运算表达式求值.docx
《四则运算表达式求值.docx》由会员分享,可在线阅读,更多相关《四则运算表达式求值.docx(21页珍藏版)》请在冰豆网上搜索。
四则运算表达式求值
HUNANUNIVERSITY
背景
在工资管理软件中,不可避免的要用到公式的定义及求值等问题。
对于数学表达式的计算,虽然可以直接对表达式进行扫描并按照优先级逐步计算,但也可以将中缀表达式转换为逆波兰表达式,这样更容易处理。
问题描述
四则运算表达式求值,将四则运算表达式用中缀表达式,然后转换为后缀表达式,并计算结果。
一.需求分析
(1)本程序利用二叉树后序遍历来实现表达式的转换,同时可以使用栈来求解后缀表达式的值。
(2)输入输出的格式:
输入:
在字符界面上输入一个中缀表达式,回车表示结束。
输出:
如果该中缀表达式正确,那么在字符界面上输出其后缀表达式和计算结果,其中后缀表达式中两相邻操作数之间利用空格隔开;如果不正确,在字符界面上输出表达式错误提示。
(3)测试用例:
输入:
21+23*(12-6)+9
输出:
2123126-*+9+
resultis168
二.概要设计
(1)抽象数据类型:
由于四则运算表达式中运算符可能有多个后继,而运算的对象无后继,可以采用二叉树来实现把中缀表达式转换为后缀表达式。
数据对象:
四则运算符及整数
数据关系:
运算符有多个后继,二运算对象的值无后继
基本操作:
后序遍历,二叉树的构建和摧毁,插入,删除
经过二叉树的后序遍历后的表达式惊醒运算是满足后进先出的原则,采用栈来实现四则运算表达式的求值。
数据对象:
运算符(字符)及整数
数据关系:
后进先出
基本操作:
入栈,出栈,栈的构建和删除
(2)算法基本思想:
用二叉树来存储四则表达式,再通过后序遍历把中缀表达式转换为后缀表达式,最后通过栈来计算表达式的值,最后输出后序表达式和表达式的值。
(3)程序的流程:
该程序有三个模块组成:
1.输入模块:
输入一个中缀表达式
2.处理模块:
把中缀表达式转换为后缀表达式
3.计算模块:
计算表达式的值
4.输出模块:
输出后缀表达式及表达式的值
三.详细设计
(1)物理数据类型:
采用指针来实现二叉树,其中分支节点存储运算符,用叶子节点存储操作数,可以减少二叉树的结构性开销。
采用顺序栈来实现表达式的计算,建立一个栈S。
从左到右读后缀表达式,如果读到操作数就将它压入栈S中,如果读到n元运算符(即需要参数个数为n的运算符)则取出由栈顶向下的n项按操作符运算,再将运算的结果代替原栈顶的n项,压入栈S中。
如果后缀表达式未读完,则重复上面过程,最后输出栈顶的数值则为结束。
伪代码:
二叉树的基本操作伪代码:
后序遍历:
template
voidnextorder(BinNode*subroot)
{
if(subroot==Null)returnfalse;//Emptysubtree,donothing
nextorder(subroot->left());
visit(subroot);//Performwhateveractionisdesired
nextorder(subroot->right());
}
二叉树的建立:
//Binarytreenodeclass
template
classBinNodePtr:
publicBinNode
{
private:
Elemit;//Thenode'svalue
BinNodePtr*lc;//Pointertoleftchild
BinNodePtr*rc;//Pointertorightchild
public:
BinNodePtr()
{lc=rc=NULL;}
BinNodePtr(Eleme,BinNodePtr*l=NULL,BinNodePtr*r=NULL)
{
it=e;
lc=l;
rc=r;
}
Elem&val()
{returnit;}
voidsetVal(constElem&e)
{it=e;}
inlineBinNode*left()const
{returnlc;}
voidsetLeft(BinNode*b)
{lc=(BinNodePtr*)b;}
inlineBinNode*right()const
{returnrc;}
voidsetRight(BinNode*b)
{rc=(BinNodePtr*)b;}
boolisLeaf()
{return(lc==NULL)&&(rc==NULL);}
};
插入及删除:
InsertChild(T,p,LR,c);
DestroyBiTree(&T);
DeleteChild(T,p,LR);
顺序栈的基本操作及伪代码:
voidinit_stack()
{top=-1;}//空线性表初始化top
voidpush_stack(intx)
{s[top]=x;}//压栈:
voidpop_stack()
{top=top-1;}//出栈
inttop_stack()
{returns[top];}//取得栈顶元素
boolempty_stack()
{
if(top==-1)
returntrue;
else
returnfalse;
}//判断栈空
boolfull_stack()
{
if(top==3)
returntrue;
else
returnfalse;
}//判断栈满
intgetHeadStack()
{
returns[top];
}//返回栈顶元素的值
}
计算表达式的值:
Statusevaluate(charch[],float&result)
{
SqStackS;
StatusSt;
inti;
i=0;
St=InitStack(S);
while(ch[i]!
='#'&&i<100)
{
if(IsDigital(ch[i]))
{
i+=EvalValue(&ch[i],S);
}
elseif(ch[i]=='')
i++;
else{
EvalExpr(ch[i],S);
i++;
}
}
(2)算法的具体步骤:
首先建立一颗二叉树,用二叉树的分支节点存储四则运算的操作符,用二叉树的叶子节点存储二叉树的操作数,然后再对二叉树进行一次后序遍历,把后序遍历得到的压入栈中,当读到n元运算符时,则取出由栈顶向下的n项按操作符运算,再将运算的结果代替原栈顶的n项,压入栈中。
如果后缀表达式未读完,则重复上面过程,最后输出栈顶的数值则为结束。
(3)算法的时空分析:
采用二叉树中分支节点和叶子节点分别存储不同的数据类型,是算法的结构性开销明显降低,对二叉树进行一次后序遍历的时间代价为
,对顺序栈实行压栈和出栈时
,故计算表达式值时时间开销为
(4)输入输出格式:
Cout<<”请输入一串中缀表达式:
”
Cin>>21+23*(12-6)+9;
Cout<<”转换为后缀表达式时为:
”<Cout<<2123126-*+9+<Cout<<”表达式的值为:
”<Cout<<168<(5)结果截图:
(6)代码:
#include
#include
#include
#include
#include
#include
#defineSTACK_INIT_SIZE100
#defineDATA_SIZE10
#defineSTACKINCREMENT10
#defineOK1
#defineTRUE1
#defineFALSE0
#defineERROR0
#defineOVERFLOW-2
usingnamespacestd;
typedeffloatSElemtype;
typedefintStatus;
typedefchar*TElemType;
typedefstructBiTNode{
TElemTypedata;
intlen;//data字符串中字符的个数
structBiTNode*lchild,*rchild;
}BiTNode,*BiTree;
typedefstruct
{
SElemtype*base;
SElemtype*top;
intstacksize;
}SqStack;
StatusIsDigital(charch)
{
if(ch>='0'&&ch<='9')
{
return1;//是数字字母
}
return0;//不是数字字母
}
intCrtNode(stack&PTR,char*c)
{
BiTNode*T;
inti=0;
T=(BiTNode*)malloc(sizeof(BiTNode));
T->data=(char*)malloc(DATA_SIZE*sizeof(char));
while(IsDigital(c[i]))
{
T->data[i]=c[i];
i++;
}
T->len=i;
T->lchild=T->rchild=NULL;
PTR.push(T);
returni;
}
voidCrtSubTree(stack&PTR,charc)
{
BiTNode*T;
T=(BiTNode*)malloc(sizeof(BiTNode));
T->data=(char*)malloc(DATA_SIZE*sizeof(char));
T->data[0]=c;
T->len=1;
T->rchild=PTR.top();//先右子树,否则运算次序反了
PTR.pop();
T->lchild=PTR.top();
PTR.pop();
PTR.push(T);
}
charsymbol[5][5]={{'>','>','<','<','>'},//符号优先级
{'>','>','<','<','>'},
{'>','>','>','>','>'},
{'>','>','>','>','>'},
{'<','<','<','<','='}};
intsym2num(chars)//返回符号对应优先级矩阵位置
{
switch(s)
{
case'+':
return0;break;
case'-':
return1;break;
case'*':
return2;break;
case'/':
return3;break;
case'#':
return4;break;
}
}
charPrecede(chara,charb)//返回符号优先级
{
return(symbol[sym2num(a)][sym2num(b)]);
}
voidCrtExptree(BiTree&T,charexp[])
{//根据字符串exp的内容构建表达式树T
stackPTR;//存放表达式树中的节点指针
stackOPTR;//存放操作符
charop;
inti=0;
OPTR.push('#');
op=OPTR.top();
while(!
((exp[i]=='#')&&(OPTR.top()=='#')))//与
{
if(IsDigital(exp[i]))
{//建立叶子节点并入栈PTR
i+=CrtNode(PTR,&exp[i]);
}
elseif(exp[i]=='')
i++;
else
{
switch(exp[i])
{
case'(':
{
OPTR.push(exp[i]);
i++;
break;
}
case')':
{
op=OPTR.top();OPTR.pop();
while(op!
='(')
{
CrtSubTree(PTR,op);
op=OPTR.top();OPTR.pop();
}//endwhile
i++;
break;
}
default:
//exp[i]是+-*/
while(!
OPTR.empty())
{
op=OPTR.top();
if(Precede(op,exp[i])=='>')
{
CrtSubTree(PTR,op);
OPTR.pop();
}
if(exp[i]!
='#')
{
OPTR.push(exp[i]);
i++;
}
break;
}
}//endswitch
}//endelse
}//endwhile
T=PTR.top();
PTR.pop();
}
voidPostOrderTraverse(BiTree&T,char*exp,int&count)
{
//后序遍历表达式树T,获取树中每个结点的数据值生成逆波兰表达式exp
//T是表达式树的根节点;字符串exp保存逆波兰表达式;count保存exp中字符的个数
//后序遍历中,处理根结点时,依据T->len的值,把T->data中的字符依次添加到当前exp字符串的尾端
//添加完T->data后,再添加一个空格字符,同时更新count计数器的值。
//填空
//inti;
if(T)
{
PostOrderTraverse(T->lchild,exp,count);
PostOrderTraverse(T->rchild,exp,count);
strncpy(exp+count,T->data,T->len);
exp[count+=(T->len)]='';
count++;
}
}
//---------------------------------
//逆波兰表达式计算
//填空
StatusInitStack(SqStack&S)
{
S.base=(SElemtype*)malloc(STACK_INIT_SIZE*sizeof(SElemtype));
if(!
S.base)exit(OVERFLOW);
S.top=S.base;
S.stacksize=STACK_INIT_SIZE;
//printf("程序运行到构建栈\n");
returnOK;
}
intStackLength(SqStackS)
{
returnS.top-S.base;
//printf("程序运行到获得堆栈元素的个数\n");
//获得堆栈元素的个数
}
StatusPush(SqStack&S,SElemtypee)
{
if(S.top-S.base>=S.stacksize)
{
S.base=(SElemtype*)realloc(S.base,(S.stacksize+STACKINCREMENT)*sizeof(SElemtype));
if(!
S.base)
exit(OVERFLOW);
S.top=S.base+S.stacksize;
S.stacksize+=STACKINCREMENT;
}
*S.top++=e;
//printf("程序运行到入栈\n");
returnOK;
//入栈
}
StatusPop(SqStack&S,SElemtype&e)
{
if(S.top==S.base)
returnERROR;
e=*--S.top;
//printf("程序运行到出栈\n");
returnOK;
//出栈
}
intEvalValue(char*ch,SqStack&S)
{
inti=0;
SElemtyperesult=0;
chara;
a=ch[i];
while(IsDigital(a))
{
result=10*result+(int)(a-48);
a=ch[++i];
}
Push(S,result);
//printf("程序运行标志1\n");
returni;
}
voidEvalExpr(charch,SqStack&S)
{
floatp,q,r;
if((ch=='+')||(ch=='-')||(ch=='*')||(ch=='/'))
{
Pop(S,p);
Pop(S,q);
switch(ch)
{
case'+':
r=p+q;
break;
case'-':
r=q-p;
break;
case'*':
r=q*p;
break;
case'/':
if(p==0)
printf("输入错误!
!
!
!
");
else
r=q/p;
break;
default:
;
}
Push(S,r);
//printf("程序运行标志2\n");
}
//如果ch中保存的是操作符,则从堆栈中弹出两个元素,并把操作符应用在这两个元素之上,
//然后把操作结果压入到栈中。
如果试图从栈中弹出两个元素是,该栈中并没有,那么该
//后缀表达式是不正确的。
}
Statusevaluate(charch[],float&result)
{
SqStackS;
StatusSt;
inti;
i=0;
St=InitStack(S);
while(ch[i]!
='#'&&i<100)
{
if(IsDigital(ch[i]))
{
i+=EvalValue(&ch[i],S);
}
elseif(ch[i]=='')
i++;
else{
EvalExpr(ch[i],S);
i++;
}
}
//如果到达表达式末尾时,栈中剩余元素不止一个,那么该
//后缀表达式是不正确的。
if(StackLength(S)==1)
Pop(S,result);
else{
//printf("表达式错误");
returnERROR;
}
//printf("程序运行标志3\n");
returnOK;
}
//-------------------------
main()
{
BiTreeT;
StatusSt;
charch[100],c;//输入的四则运算表达式
charexp[100];//逆波兰表达式
intcount=0;
inti=0;
floatresult;
printf("请输入表达式:
\n");
while(i<100)
{
scanf("%c",&c);
ch[i++]=c;
if(c=='\n'){
ch[--i]='#';
break;
}//endif
}
CrtExptree(T,ch);//根据字符串ch的内容构建表达式树T
//后序遍历表达式树T得到表达式的逆波兰表达式exp;count中保存exp中字符的个数
PostOrderTraverse(T,exp,count);
printf("逆波兰表达式为:
\n");
for(i=0;iprintf("%c",exp[i]);
printf("\n");
exp[count]='#';//添加结束符
St=evaluate(exp,result);//计算逆波兰表达式exp的值
//输出计算的结果
if(St)
printf("运算结果:
%5.2f\n",result);
else
printf("\n表达式错误\n");
return0;
}