问题:算数运算的表达式求知
这道题不难,但是当你认真去编代码的时候,还是要考虑好多细节。
算法原理如下:
我们都知道算术四则运算的运算规则是:
先乘除,后加减。
从左到右计算
先算括号内,再算括号外
表达式组成
任何一个表达式都有操作数、运算符和界定符组成。
操作数即可以是常量,也可以是被说明为变量或常量的标识符。
运算符可以分为算术运算,关系运算和逻辑运算符。
界定符有左右括号和结束符等。
本文为了方便演示只使用算术运算。
运算符优先级
对于连个相继出现的操作符θ1和θ2 有三种关系:大于、等于和小于。由此可以列出“+-*/”之间的优先级。如下表:
+ | - | * | / | ( | ) | # | |
+ | > | > | < | < | < | > | > |
- | > | > | < | < | < | > | > |
* | > | > | > | > | < | > | > |
/ | > | > | > | > | < | > | > |
( | < | < | < | < | < | = | |
) | > | > | > | > | > | > | |
# | < | < | < | < | < | = |
加减乘除优先性都低于“(”但是高于“)”,由运算从左到右可知,当θ1=θ2 ,令θ1>θ2
为了算法简洁,在表达式的左边和右边虚设一个“#”,这一对“#”表示一个表达式求值完成。
“(”=“)”当一对括号相遇时表示括号内已运算完成。
“)”和“(”、“#”和“(”、“(”和“#”无法相继出现如果出现则表达式出现语法错误。
为实现优先算法,可以使用两个工作栈,一个是OPTR,用于寄存运算符,一个是OPND,用于寄存运算数和运算结果。
代码如下:(存在一个bug)
#include<stdio.h> //表达式求值,顺序栈 #include<stdlib.h> #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10 #define OK 1 #define ERROR 0 typedef int Status; typedef struct{ char * base; char * top; int stacksize; }SqStack; //函数声明.................................. Status InitStack(SqStack &S); //符号栈的建立 Status Push(SqStack &S,char e); //入栈 void OperandType_EvaluateExpression(SqStack &OPND,SqStack &OPTR); char GetTop(SqStack S); //返回栈顶元素 Status Pop(SqStack &S,char &e); //出栈 Status IsDigit(char c); //判断是否为数字 char Precede(char a,char e); //符号优先级判断, int swap_char_to_int(char a); //在Precede中的字符转换为数字函数 char Operate(char a,char x,char b); //运算函数,返回为char型的数字 Status shownumbers(SqStack S); //函数实现........................................... Status InitStack(SqStack &S) { S.base = (char *)malloc(STACK_INIT_SIZE * sizeof(char)); if(!S.base) exit(ERROR); S.top = S.base; S.stacksize = STACK_INIT_SIZE; return OK; } Status Push(SqStack &S,char e) { if(S.top - S.base >= S.stacksize) { S.base = (char *)realloc(S.base,(S.stacksize + STACKINCREMENT) * sizeof(char)); if(!S.base) exit(ERROR); S.top = S.base + S.stacksize; S.stacksize += STACKINCREMENT; } *S.top = e; S.top++; return OK; } void OperandType_EvaluateExpression(SqStack &OPND,SqStack &OPTR) { char c; char x; char b; char a; Push(OPTR,'#'); c = getchar(); while(c != '#' || GetTop(OPTR) != '#') { if(IsDigit(c)) //若是数字则进数字栈,不是判断符号优先级 { Push(OPND,c); c = getchar(); } else { switch(Precede(GetTop(OPTR),c)) { case '<': Push(OPTR,c); c = getchar(); break; case '=': Pop(OPTR,x); c = getchar(); break; case '>': Pop(OPTR,x); Pop(OPND,b); Pop(OPND,a); Push(OPND,Operate(a,x,b)); break; // printf("") } } } /* char sum; sum = GetTop(OPND); printf("%c\n",sum); shownumbers(OPND); printf("\n"); shownumbers(OPTR); */ } char GetTop(SqStack S) { char e; if(S.top == S.base) exit(-1); else e = *(S.top-1); return e; } Status Pop(SqStack &S, char &e) { if(S.base == S.top) return ERROR; else e = *--S.top; return OK; } Status IsDigit(char c) { if(c >= '0' && c <= '9') return 1; else return 0; } char Precede(char a,char e) { int i,j; int flag; int symbol[7][7] = { {0,0,1,1,1,0,0}, {0,0,1,1,1,0,0}, {0,0,0,0,1,0,0}, {0,0,0,0,1,0,0}, {1,1,1,1,1,2,4}, {0,0,0,0,4,0,0}, {1,1,1,1,1,4,2}, }; i = swap_char_to_int(a); j = swap_char_to_int(e); flag = symbol[i][j]; switch(flag) { case 0:return '>'; case 1:return '<'; case 2:return '='; case 4:exit(-1); } } int swap_char_to_int(char a) { switch(a) { case '+':return 0; case '-':return 1; case '*':return 2; case '/':return 3; case '(':return 4; case ')':return 5; case '#':return 6; } } char Operate(char a,char x,char b) { int c,d; int sum; c = a-'0'; d = b-'0'; switch(x) { case '+':sum = c+d;break; case '-':sum = c-d;break; case '*':sum = c*d;break; case '/':sum = c/d;break; } return sum+'0'; } Status shownumbers(SqStack S) { int numbers = 1; if(S.top == S.base) { printf("栈为空栈,无法打印\n"); exit(-1); } while(S.base < S.top) { S.top--; printf("%c ",*S.top); numbers++; } } //驱动函数.................. int main() { int sum; SqStack OPTR; //符号栈 SqStack OPND; //数字栈 InitStack(OPTR); InitStack(OPND); OperandType_EvaluateExpression(OPND,OPTR); sum = GetTop(OPND) - '0'; printf("%d",sum); return 0; }
运行结果如下:
1*2+6/2*(2+3*5)#
53--------------------------------
Process exited after 16.61 seconds with return value 0
请按任意键继续. . .
存在BUG如下:
1,输入的整数必须小于10.
2,不能输入小数,除数必须大于被除数。
分析:
1.代码中由于通过getchar()读取,所以当输入10/5的时候,读取的其实是1 和 0 两个字符。
2.这个是因为int型的除法问题,比如当3/7的时候,并不是小数,而是得0。
对于的一个bug,我想到的是,可以在输入的时候,建立数组完成,但是这个会导致输入有限,或者继续用getchar()输入,输入后进行数字的判断来解决,想一想是可以实现的。如果是连续的数字,就累加,终止条件是下一个符号。
对于第二个bug,则是要建立float型的数据类型,把char型转换为float型,但是或许又潜在的问题?并没有尝试。