所谓表达式的求值就是从键盘上输入一个四则运算表达式按下Enter后在屏幕上输出表达式的结果。表达式的求值在计算机的应用中非常广泛,例如编译器中对所写的程序表达式的编译等。它也是数据结构课程中栈这一章节中非常重要的一个算法,通过实现这个算法可以更好的掌握和理解栈的相关操作。
中缀表达式是指运算符在运算数的中间,计算中缀表达式时需要用到两个栈:数字栈和运算符栈。在整个中缀表达式求值的过程中主要涉及到的模块有:栈的相关操作、优先级表的确立、输入的待计算字符串拆分为数字和运算符以及运算处理等。
一)、整体算法思路
1)、设立操作数栈和运算符栈,设表达式结束的标志是字符#,运算符栈底初始化为#,约定#运算符的优先级最小(这样做的目的是在当两个#相遇时就可以确定表达式扫描结束了)。
2)、若当前扫描到的是操作数则果断将此数压栈进操作数栈,如果当前是符号栈则将该操作符和栈顶操作符进行优先级比较如果低于栈顶优先级则将操作符栈顶元素弹出并弹出两个操作数进行运算,运算完毕将结果压入栈中。如果当前符号的优先级高于栈顶优先级则将此运算符入栈。
3)、循环操作2直到输入的表达式运算结束(运算符栈底的#和输入的表达式的#相遇)此时如若操作数栈中只剩一个数字则表示运算成功,此数就是表达式的结果,如果不止一个数则表示输入的表达式有误。
二)、优先级表
根据程序的整体思路可知只有当前扫描到的运算符优先级小于运算符栈顶的运算符时才能将栈顶运算符进行运算,这也就是说我们要在同一运算级上运算符的优先级做一些小小的修改(将同一运算级上的运算符看作栈中的优先级总大于此时扫描的运算符)才能满足同一运算级上从左至右运算的规则。因此就可以得到下表(比较顺序为当前扫描比较栈中):
当前扫描 栈中 |
+ |
– |
* |
/ |
( |
) |
# |
+ |
< |
< |
> |
> |
> |
< |
< |
– |
< |
< |
> |
> |
> |
< |
< |
* |
< |
< |
< |
< |
> |
< |
< |
/ |
< |
< |
< |
< |
> |
< |
< |
( |
> |
> |
> |
> |
> |
= |
< |
) |
|
|
|
|
|
|
|
# |
> |
> |
> |
> |
> |
误 |
|
三)、输入的字符串拆分成数字和运算符
另一个难点在于将输入的字符串拆分成数字和运算符,可以将此过程放入运算过程中进行,因为拆分出来的数字和运算符需要判断是入栈还是运算,如果将拆分出来的数字和运算符重新存起来在进行运算操作的话太麻烦和太耗费空间。
具体的拆分方法可以采用while循环的方式,定义一个double型变量用来存储目前获取到的数据,用一个char型变量ch每次从输入缓存区中读取一个字符,再进行判断,如果是数字则将每次得到的字符转换成int型后放入data的个位,如果是运算符则进行运算符的优先级比较,如果是小数点则判断data中是否已经存在小数点,如果存在则报错,如果不存在则加入权位,权位最开始为0.1以后在出现运算符之前每次都给权位乘以0.1,在将其加入到data中时乘以权位即可。
代码实现:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXSIZE 100 //数字栈 typedef struct oprd{ double data[MAXSIZE]; int top; }OPRD; //运算符栈 typedef struct optr{ char data[MAXSIZE]; int top; }OPTR; //因为涉及到两个栈的操作,所以将栈相关的操作用宏定义写成函数, //这样就具有了通用性 //初始化栈 #define InitStack(StackType, stack) \ { \ *stack = (StackType *)malloc(sizeof(StackType)); \ *stack->top = -1; \ } //判栈空 #define EmptyStack(stack) \ ( \ stack->top == -1 \ ) //判栈满 #define FullStack(stack) \ ( \ stack->top == MAXSIZE - 1 \ ) //入栈 #define PushStack(stack, value) \ { \ if (!FullStack(stack)){ \ stack->top++; \ stack->data[stack->top] = value; \ } \ else{ \ printf("栈已满,无法入栈\n"); \ exit(-1); \ } \ } //出栈 #define PopStack(stack, value) \ { \ if (!EmptyStack(stack)){ \ *value = stack->data[stack->top]; \ stack->top--; \ } \ else{ \ printf("栈已空,无法出栈\n"); \ exit(-1); \ } \ } //取栈顶元素 #define GetStackTop(stack, value) \ { \ if (!EmptyStack(stack)){ \ *value = stack->data[stack->top]; \ } \ else{ \ printf("栈为空,无法取栈顶元素\n"); \ } \ } //优先级表 char compare(char ch, char top) { switch(ch){ case '+': case '-': if (top == '+' || top == '-' || top == '*' || top == '/') return '<'; //扫描的小于栈顶 else return '>'; //扫描的大于栈顶 break; case '*': case '/': if (top == '*' || top == '/') return '<'; else return '>'; break; case '(': if(top == ')'){ printf("输入有误!\n"); exit(-1); } return '>'; break; case ')': if (top == '(') return '='; else if(top == '#'){ printf("输入有误!\n"); exit(-1); } else{ return '<'; } break; case '#': return '<'; } } //输入表达式并计算结果 double CalculateExp(void) { double result, tempNum1, tempNum2; double data = 0, expn; char ch, topSign, point = 'n', num = 'n'; OPTR *sign; OPRD *number; InitStack(OPTR, &sign); InitStack(OPRD, &number); PushStack(sign, '#'); printf("请输入表达式:"); ch = getchar(); GetStackTop(sign, &topSign); while(ch != '#' || topSign != '#'){ if ('0' <= ch && ch <= '9' || ch == '.'){ if (ch == '.' && point == 'y'){ printf("表达式输入有误!\n"); exit(-1); } else if (ch == '.' && point == 'n'){ point = 'y'; expn = 0.1; } else{ if (point == 'y'){ data = data + expn * (ch - '0'); expn *= 0.1; } else{ data = data * 10 + (ch - '0'); } num = 'y'; } ch = getchar(); } else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')' || ch == '#'){ if (num == 'y'){ PushStack(number, data); num = 'n'; point = 'n'; data = 0; } GetStackTop(sign, &topSign); switch(compare(ch, topSign)){ case '<': //扫描运算符优先级小于栈顶元素 PopStack(sign, &topSign); PopStack(number, &tempNum1); PopStack(number, &tempNum2); switch(topSign){ case '+': result = tempNum1 + tempNum2; break; case '-': result = tempNum1 - tempNum2; break; case '*': result = tempNum1 * tempNum2; break; case '/': result = tempNum2 / tempNum1; break; } PushStack(number, result); break; case '>': //扫描运算符优先级大于栈顶元素 PushStack(sign, ch); ch = getchar(); break; case '=': //扫描运算符为右括号,匹配到了左括号 PopStack(sign, &topSign); ch = getchar(); break; } } else if (ch == '\n'){ ch = '#'; } else{ printf("输入的表达式有误!\n"); exit(-1); } GetStackTop(sign, &topSign); } PopStack(number, &result); //将结果从栈中取出来 if (!EmptyStack(number)){ //如果取出后栈不为空则表示输入的表达式不正确 printf("表达式有误!\n"); exit(-1); } return result; } int main(void) { printf("%lf\n", CalculateExp()); return 0; }