基于广义表的算术表达式的实现

来源:互联网 发布:mdraw软件 编辑:程序博客网 时间:2024/06/05 20:17

                   看过数据结构书上有个算术表达式的实现,觉得实现比较麻烦,用到了队列,栈还有一个难以建立的运算符优先级比较表,在最初看那个本书的时候写了估计一天,最近又在重新温习数据结构,再看打那个表达式的时候,突然想到可以用广义表通过递归建立和运算得到运算结果,实现起来感觉思想上还是比较容易理解的,可能是算法的基础还有有点薄弱,下面的代码几乎写了6个小时,弱爆了。

                 写的时候发现,百度也可以直接对表达式给出运算结构,框计算太牛逼了····

 

.h   文件·

/* 定义节点类型,三中情况,数据、操作符、链表 */typedef enum{OPND,OPTR,LIST} ElemTag;typedef double Number;typedef char Operator;typedef struct node{/* tag用来标识改节点的类型 */ElemTag  tag;/* 节点的数据,共用体表示 */union{Number data;Operator  optr;struct node * hp;};struct node * next;}GList , * pGList;int GetNext(char * str,char * host,int* NumOrOpt,int Count);int CreateList(pGList  *L ,char* S);int isNum(char c);#include <math.h>/* 根据传入的操作符和两个运算数返回结果 */double Calculate(double opnd1,double opnd2,char optr){switch( optr ){case '+':return opnd1 + opnd2;break;case '-':return opnd1 - opnd2;break;case '*':return opnd1 * opnd2;break;case '/':return opnd1 / opnd2;break;default:break;}}/*对传进来的表达式字符串通过递归操作*/int CreateList(pGList * L,char * str){char host[20] = {0};int NumOrOpt = 0;int Count = 0 ;pGList  p;if( !(L) ) return 0;*L =  ( GList*) malloc(sizeof(GList));p = *L;memset(p,0,sizeof(GList) );/* 将表达式存入到广义链表,带括号的话又递归存入到子链表 ,每个链表的第一个是不存储数据是*/while( str[Count] != 0 ){Count=GetNext(str,host,&NumOrOpt,Count);/*printf("%4d,%s NumOrOpt=%d\n",Count,host,NumOrOpt);*//*遇到右括号表示结束,不在继续分配内存*/if( NumOrOpt < 6 ){p ->next = ( GList*) malloc(sizeof(GList));memset(p->next,0,sizeof(GList) );}/* 根据不同的NumOrOpt数值,标识当前host装入的不同类型数据如果是一个 左括号 (  开始一个新的子链表,如果是右括号)表示当前链表结束*/switch( NumOrOpt ){case 0 :p->next->tag = OPND;p->next->data = atof(&host);break;case 1:p->next->tag = OPTR;p->next->optr= host[0];break;case 2 :p->next->tag = LIST;Count += CreateList(&(p->next->hp),&str[Count]);/* 将分析了字符个数加到Count上,以便下次分析 */break;case 3 :return Count;/* 返回当前链表分析了的字符个数 */break;default: break;}p = p ->next;memset(host,0,20);}return 0;}/*对存储了运算表达式的广义链表计算,最后得到的答案就在广义链表的头结点的数据域data*/void CalList(GList  * lp){GList * pt = 0 ;GList * pa = 0 ;int i,flag ;pt= lp->next;/* 搜索最里层的单项表达式(没有括号的) */while(pt){if( pt->tag == LIST ){CalList(pt->hp);pt->tag = pt->hp->tag;pt->data = pt->hp->data;}pt = pt->next;}/* 将最里层的单项表达式的结果运算出来,存放到该链表自身的头节点,同时清除已经被运算了的节点,头结点是没有数据的*/i = 2;while(i>0)/* 这里的运算是在确保没有括号的情况下 先对乘除运算,同时去除被运算了的数据节点 */{pt  = lp->next ;while(( pt && pt->next&& pt->next->next )){if( !(pt->next && pt->next->next )){printf("Error expression!! %c\n",pt->optr);exit(1);}/* 第二次运算就是对加减运算了  */if( pt->next->tag == OPTR && (( pt->next->optr == '*' || pt->next->optr == '/') || i == 1 )){pa = pt->next;pt->data = Calculate(pt->data,pa->next->data,pa->optr);pt->next = pt->next->next->next;free(pa->next);free(pa);continue;}pt = pt->next;}--i;}/* 结果保存到子链表的本身处 */lp->tag = OPND;lp->data = lp->next->data;free(lp->next);lp->next = 0;}/*输出广义链表*/void ShowList(GList  * lp){GList * pt = 0 ;pt  = lp->next ;while(pt){if( pt->tag == LIST ){ShowList(pt->hp);}if( pt->tag == OPND )printf("%lf ",pt->data);if( pt->tag == OPTR )printf("%c ",pt->optr);pt = pt->next;}printf("\n");}/*从第Count个元素开始分析,如果后面紧接着一个数据就通过host返回,NumOrOpt为1-6表示当前host存储了不同的类型的数据0:存储的是数据,1到4表示+、-、*、/四种操作,5 6分别表示 ( )*/int GetNext(char * str,char * host,int* NumOrOpt,int Count){int   flag ,n ,epos;if(!(str && host && NumOrOpt))return 0;n =  Count ;flag = IsNum(str[n]);while( str[n] && flag == IsNum(str[n]) ){if(   ')' == str[n++]  ||   '(' == str[n]  )break;}/*把从Count处开始分析的字符串保存到host,要么是一个数值的字符串,有么是一个操作符*/strncpy(host,&str[Count],n-Count);switch( host[0] ){case '+' :*NumOrOpt = 1;break;case '-' :*NumOrOpt = 1;break;case '*' :*NumOrOpt = 1;break;case '/' :*NumOrOpt = 1;break;case '(' :*NumOrOpt = 2;break;case ')' :*NumOrOpt = 3;break;default: *NumOrOpt = 0;}return n;}/*判断字符c是否为字符*/int IsNum(char c){if(( c >= '0' && c <= '9') || '.' == c )return 1;return 0;}


验证结果的

#include "biaodashi.h"main(){char str[100]="((5)*19*6*(6*(4*(5*9-4/(9-2))))+2*(1.5*(18.4+1.6*(2-1)))/10-52*(5+2*0.25*9))";GList * L;printf("Please Input a legal expression!\n");//gets(str);printf("%s \n",str);CreateList(& L, str);CalList(L);printf("The Answer is  %lf\n",L->data);}
 
运算结果:
可以直接在百度搜索框里面输入(5)*19*6*(6*(4*(5*9-4/(9-2))))+2*(1.5*(18.4+1.6*(2-1)))/10-52*(5+2*0.25*9)) 得到的同样的运算结果,小数点后面若干位就不管了。