您的位置:首页 > 编程语言 > C语言/C++

c语言栈实现表达式求值

2016-08-12 17:03 302 查看

算术四则运算规则

先乘除,后加减

从左算到右

先括号内后括号外

表达式组成

任何一个表达式都有操作数、运算符和界定符组成。

操作数即可以是常量,也可以是被说明为变量或常量的标识符。

运算符可以分为算术运算,关系运算和逻辑运算符。

界定符有左右括号和结束符等。

若把运算符和界定符统称为算符,他们构成的集合命名为OP.则任意两个相继出现的算符a和b之间的有限关系至多是下面的三种之一:

a < b   a的优先权高于b

a < b   a的优先权低于b

a = b   a的优先权等于b

OP的优先权关系如下表(0表示不存在)

+-*/()#
+>><<<>>
>><<<>>
*>>>><<<
/>>>><<<
(<<<<<=0
)>>>>0>>
#<<<<<0=
加减乘除优先性都低于“(”,但是高于“)”,为了算法简洁,在表达式的左边和右边虚设一个“#”,
这一对“#”表示一个表达式求值完成
“(”=“)”表示当一对括号相遇时表示括号内已运算完成。
“)”和“(”、“#”和“(”、“(”和“#”无法相继出现如果出现则表达式出现语法错误。

为实现优先算法,可以使用两个工作栈,一个是OPTR,用于寄存运算符,一个是OPND,用于寄存运算数和运算结果。

##算法基本思路

1.首先置操作数栈为空栈,表达式起始符为“#”为栈底元素。

2.依次读入表达式中的每个字符,若是操作数则进OPND栈,若是运算符则和OPTR栈的栈顶运算符比较优先权作相应操作:

1. 栈顶元素优先权低:运算符入栈,并重新输入
2. 优先权一样:运算符弹栈,并重新输入
3. 栈顶元素优先权高:弹栈,计算,结果入栈

直至整个表达式求值完毕(OPTR栈顶元素和当前读入的字符均为“#”)

##代码实现
参考严蔚敏老师的《数据结构》(c语言版)的实现思路,代码由自己完成

#include"int_sp_stack.c"
#include <ctype.h>
/* 存储运算符和界限符 */
char OP[7] = {'+', '-', '*', '/', '(', ')', '#'};
/* OP的优先级表 */
char pre[][7] ={
{'>', '>', '<', '<', '<', '>', '>'},
{'>', '>', '<', '<', '<', '>', '>'},
{'>', '>', '>', '>', '<', '>', '>'},
{'>', '>', '>', '>', '<', '>', '>'},
{'<', '<', '<', '<', '<', '=', '0'},
{'>', '>', '>', '>', '0', '>', '>'},
{'<', '<', '<', '<', '<', '0', '='}
};

/* 输入函数
* 若输入运算数(可以是多位数),存到指针n中,并返回true
* 若输入运算符,存到指针c中,并返回false
*/
int get_input(int *n)
{
*n = 0;
char ch = getchar();
/* 消除\n */
getchar();
if(!isdigit(ch))
{
*n = ch;
return true;
}
do
{
*n = (*n) * 10 + (ch - '0');
ch = getchar();
/* 消除\n */
getchar();
}
while(ch != ' ');
return false;
}

/* 判断输入运算符和栈顶运算符的优先级
* 若栈顶运算符的优先级高返回'>'
* 若栈顶运算符的优先级低返回'<'
* 若栈顶运算符和输入运算符的优先级相等返回'='
* 否则返回'0'
*/

char precmp(char s_top, char ch)
{
int i, j;
switch(s_top)
{
case '+':i = 0;break;
case '-':i = 1;break;
case '*':i = 2;break;
case '/':i = 3;break;
case '(':i = 4;break;
case ')':i = 5;break;
case '#':i = 6;break;
default:i =0;break;
}
switch(ch)
{
case '+':j = 0;break;
case '-':j = 1;break;
case '*':j = 2;break;
case '/':j = 3;break;
case '(':j = 4;break;
case ')':j = 5;break;
case '#':j = 6;break;
default:j =0;break;
}
return pre[i][j];
}

/* 根据两个栈的弹栈内容计算 */
int compute(int a, char theta, int b)
{
int result;
switch(theta)
{
case '+':result = a + b;break;
case '-':result = a - b;break;
case '*':result = a * b;break;
case '/':result = a / b;break;
}
return result;

}

/* 算数表达式求值的算符优先算法
* 设OPTR和OPND分别为运算符栈和运算数栈
*/

int evaluate_expression()
{
/* 输入元素 */
int *elem = (int *)malloc(sizeof(*elem));
/* 输入元素类型
* type = true表示输入为运算符
* type = false表示输入为运算数
*/
int type;
/* 出栈元素 */
int *x = (int *)malloc(sizeof(*x));
/* 运算出栈元素 */
int *theta = (int *)malloc(sizeof(*theta));
int *a = (int *)malloc(sizeof(*a));
int *b = (int *)malloc(sizeof(*b));
/* 操作符栈 */
int_sp_stack OPTR = (int_sp_stack)malloc(sizeof(int_stack));
/* 操作数栈 */
int_sp_stack OPND = (int_sp_stack)malloc(sizeof(int_stack));
OPTR = stack_init(OPTR);
OPND = stack_init(OPND);
push(OPTR, '#');
type = get_input(elem) ;
*x = '#';
while(*elem != '#' || *x != '#')
{
/* 输入不是运算符 */
if(!type)
{
push(OPND, *elem);
type = get_input(elem);
}
else
{
get_top(OPTR, x);
switch(precmp(*x, *elem))
{
case '<':
push(OPTR, *elem);
type = get_input(elem);
get_top(OPTR, x);
break;
case '=':
pop(OPTR, x);
get_top(OPTR, x);
type = get_input(elem);
break;
case '>':
pop(OPTR, theta);
pop(OPND, b);
pop(OPND, a);
push(OPND, compute(*a, *theta, *b));
get_top(OPTR,x);
break;
}
}

}
get_top(OPND, x);
stack_destroy(OPTR);
stack_destroy(OPND);
return *x;
}
int main()
{
int x = evaluate_expression();
printf("x = %d", x);
return 0;
}


注意:对于getchar()函数来说,是从缓存流中获取一个字符并返回的.例如连续调用两次getchar()函数

char a = getcahr();
char b = getchar();


当执行第一个getchar()函数时,输入x并以回车键结束输入,这时当程序调用第二个getchar()函数时,回直接从缓存那取得换行符’\n’返回,也就是b=’\n’,而不会终止程序,等待从键盘再输入一个字符.想要从键盘输入一个字符赋给b,应先消除缓存中’\n’的影响,较简单的方法是在两个getchar()函数之间再调用一次getchar()函数.

上述程序,采用的是之前顺序栈的实现接口,程序正确执行的输入格式:例如计算(52 - 3 + 1) * 10

(
5
2
"空格"
-
3
"空格"
-
1
"空格"
)
*
1
0
"空格"
#
"回车"


结果截图:



改进输入:把int get_input(int n)函数改成下面的形式后,程序运行时输入格式为:例如计算(52 - 3 + 1) 10

int get_input(int *n)
{
*n = 0;
char ch = getchar();
/* 消除\n */
//getchar();
if(!isdigit(ch))
{
*n = ch;
getchar();
return true;
}
do
{
*n = (*n) * 10 + (ch - '0');
ch = getchar();
/* 消除\n */
//getchar();
}
while(ch != '\n');
return false;
}


(
52
-
3
-
1
)
*
10
#
"回车"


结果截图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: