/**************************************************************************************************************** 逆波兰式的作用: 对于实现逆波兰式算法,难度并不大,但为什么要将看似简单的中序表达式转换为复杂的逆波兰式? 原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中序表达式是非常复杂的结构。 相对的,逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。 如:我们平时写a+b,这是中缀表达式,写成后缀表达式就是:ab+ (a+b)*c-(a+b)/e的后缀表达式为:如:我们平时写a+b,这是中缀表达式,写成后缀表达式就是:ab+ (a+b)*c-(a+b)/e的后缀表达式为: (a+b)*c-(a+b)/e →((a+b)*c)((a+b)/e)- →((a+b)c*)((a+b)e/)- →(ab+c*)(ab+e/)- →ab+c*ab+e/- →ab+c*ab+e/- 算法实现: 将一个普通的中序表达式转换为逆波兰表达式的一般算法是: 首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),S1栈可先放入优先级最低的运算符#, 注意,中缀式应以此最低优先级的运算符结束。可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤: (1)若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈;若取出的是运算符,并且当前S1栈顶为(,则当前运算符直接入S1栈。 (2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符优先级大于S1栈栈顶运算符优先级,则将该运算符进S1栈, 否者,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,则将该运算符送入S1栈。 (3)若取出的字符是“(”,则直接送入S1栈栈顶。 (4)若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。 (5)重复上面的1~4步,直至处理完所有的输入字符 (6)若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。 完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。便可以按照逆波兰式的计算方法计算了! *****************************************************************************************************************/
#include <stdio.h> #include <string.h> #include <time.h> /*记录运行时间*/ #include "栈的链式存储.c" struct sNode *symbol; struct sNode *result; /* 1.符号优先级获取函数*/ int getPriority(char op) { switch(op) { case '+': case '-': return 1; case '*': case '/': return 2; case '(': /*不会有判断')'的情况*/ return 0; default: return -1; } } /* 2.初始化栈*/ int init2stack() { initStack(&symbol); initStack(&result); } /* 3.解析原始表达式,使之变成逆波兰式表达式(将表达式保存到result栈中,栈底为表达式左端).*/ void transform(char *str) { elemType x,y; int i = 0; int priority = -1; /*遍历字符串*/ while(i < strlen(str)) /*以换行符表示表达式结束,若表达式是通过外部输出,可更换*/ { /*如果是操作数,直接压入result栈*/ if(str[i] >= '0' && str[i]<='9') { x = 0; while(str[i] >= '0' && str[i]<='9') { x = x*10 + str[i] - 48; i++; } if(str[i] == '.') { i++; y =0; double j = 10.00; while(str[i] >= '0' && str[i]<='9') { y = y + str[i]/j; i++; } x += y; } push(&result,x); } if(i >= strlen(str)) { break; } /*如果是运算符 : 1.'('直接放入symbol栈 2. ')'将symbol栈中最靠近栈顶的'(' 与栈顶见得运算符,从顶到底依次写入result栈.并pop出'(' 3. '-'或'+' 判断symbol栈顶运算符(小括号'('除外)的优先级,比'+-'小,就将'+-'压入栈symbol.比'+-'大于等于的,pop出来,压入result栈. 并将'+-'压入symbol栈 4.'*'或'/'同第3条规则. 5.若symbol栈为空,直接压栈 */ switch(str[i]) { case '(': push(&symbol,'('); break; case ')': while( (char)peek(&symbol) != '(') /*double型的元素要强转一下*/ { push(&result,pop(&symbol)); /*将( ) 之间的运算符导入result*/ } pop(&symbol); break; case '+': case '-': case '*': case '/': if(emptyStack(&symbol)) /*如果栈空,直接压栈*/ { push(&symbol,str[i]); }else{ priority = getPriority(str[i]); if(priority <= getPriority((char)peek(&symbol))) /*若symbol栈顶元素优先级大,基本是栈顶为*'/',此时str[i]为+-的情况*/ { push(&result,pop(&symbol)); push(&symbol,str[i]); }else{ /*栈顶为+-,str[i]为*'/'的情况*/ push(&symbol,str[i]); } } break; default: break; } i++; } /*结束遍历原始表达式*/ while(!emptyStack(&symbol)) /*将symbol中的所有剩余运算符送入result栈*/ { push(&result,pop(&symbol)); } } /* 4.使存储逆波兰式的栈中数据倒置,用于挨个取出进行分析计算(将result中表达式倒置入symbol栈中.表达式右端在栈顶).*/ void dataInversion() { while(!emptyStack(&result)) /*将result中的逆波兰式倒置进symbol,以便进行最终结果计算时使用*/ { push(&symbol,pop(&result)); } } /* 5.分析计算函数*/ double compute() { elemType x,y; /*用于保存数字,x为整数部分,y为小数部分.(如果是小数的话)*/ elemType temp; int i; /*用于遍历字符串*/ while(emptyStack(&symbol) != 1) { temp = pop(&symbol); if((char)temp == ' ') /*如果是空格,不做操作*/ { i++; continue; } switch((char)temp) { case '+': x = pop(&result); x = x + pop(&result); i++; break; case '-': x = pop(&result); x = pop(&result) - x; i++; break; case '*': x = pop(&result); x = x * pop(&result); i++; break; case '/': x = pop(&result); if(x>0.000001 && x<-0.000001) /*不可以将浮点变量用“==”或者“!=”与任何数字比较。*/ { printf("除数是0,终止程序.\n"); system("pause"); }else{ x = pop(&result) / x; } break; default: x = temp; break; } /*switch 判断当前字符结束*/ push(&result,x); } /*str字符串遍历结束*/ x = pop(&result); return x; } /* 6.遍历整个栈,从底到顶*/ void travelStack(struct sNode *hs) { struct sNode *temp = hs; char arr[100]; int i,n =0; while(temp != NULL) { arr[n++] = temp->data; temp = temp->next; } for(i = n-1; i>=0; i--) { switch((char)arr[i]) { case '+': case '-': case '*': case '/': case '(': case ')': printf("%c ",(char)arr[i]); break; default: printf("%d ",(int)arr[i]); break; } } } /* 7.检查表达式格式,通过返回1,否则返回0*/ int checkExpression(char *str) { int i = 0; int len = strlen(str); int head = 0; int parenthesis =0; while(i < len) { /*如果是操作数,直接压入result栈*/ if(str[i] >= '0' && str[i]<='9') { while(str[i] >= '0' && str[i]<='9') { i++; } if(str[i] == '.') { i++; while(str[i] >= '0' && str[i]<='9') { i++; } } if(head == 0) head = 1; /*数字后面必须是+ - * '/' 或者,数字为表达式结尾时除外)*/ if(str[i] != 42 && str[i] != 43 && str[i] != 45 && str[i] != 47 && i != len) { if( str[i] != 41) { printf("表达式错误... %c a附近\n",str[i]); return 0; } } } if(i >= strlen(str)) { return 1; } switch(str[i]) { case '+': case '-': case '*': case '/': /* '+''-''*''/'后面只可以是'('或者操作数,且不能在表达式开头,或结尾*/ if(head == 0 || i== len -1) { printf("表达式错误... %c 附近\n",str[i]); return 0; } if((str[i+1] < '0'|| str[i+1] > '9' )) { if(str[i+1] != '(') { printf("表达式错误... %c 附近\n",str[i]); return 0; } } break; case '(': if(head == 0) head =1; /* '+''-''*''/'后面只可以是'('或者操作数,且不能在表达式开头,或结尾*/ if(i== len -1) { printf("表达式错误... %c 附近\n",str[i]); return 0; } if(str[i+1] < '0' || str[i+1] > '9' ) { printf("表达式错误... %c 附近\n",str[i]); return 0; } parenthesis++; break; case ')': /* ')'后面只可以是运算符*/ if(i == len -1) break; if((str[i+1] != 42 && str[i+1] != 43 && str[i+1] != 45 && str[i+1] != 47) || head == 0) { printf("表达式错误... %c 附近\n",str[i]); return 0; } parenthesis--; if(parenthesis < 0) { printf("表达式错误... 缺少与 ) 匹配的 (\n"); return 0; } break; default: printf("不明运算符: %c\n",str[i]); return 0; } i++; } if(parenthesis < 0) { printf("表达式错误... 缺少与 ) 匹配的 (\n"); return 0; }else if(parenthesis > 0) { printf("表达式错误... 缺少与 ( 匹配的 )\n"); return 0; } return 1; } int main(void) { double cost_time; clock_t start,end; /*时间毫秒级*/ char *str = "(1+2)*3-(5+6)/4"; elemType res; start=clock(); printf("表达式为:\n\t%s\n",str); printf("正在检查表达式格式...\n"); if(checkExpression(str) == 0) { system("pause"); } printf("将表达式转换成逆波兰式(后缀式)...\n\t"); transform(str); travelStack(result); printf("\n进行倒置\n\t"); dataInversion(); travelStack(symbol); res = compute(); printf("\n结果为 : %f\n",res); end=clock(); cost_time=(double)(end-start)/CLOCKS_PER_SEC; printf("所用时间: %f\n",cost_time); system("pause"); return 0; }
******************************************************************************
运行结果:
******************************************************************************
表达式为: (1+2)*3-(5+6)/4 正在检查表达式格式... 将表达式转换成逆波兰式(后缀式)... 1 2 + 3 * 5 6 + 4 / - 进行倒置 - / 4 + 6 5 * 3 + 2 1 结果为 : 6.250000 所用时间: 0.002000 请按任意键继续. . .