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