栈的应用举例--表达式求值
我们仅讨论简单算术表达式的求值问题,这种表达式只含加、减、乘、除 4 种运算符。
算符间的优先无关系:
+ | - | * | / | ( | ) | # | |
---|---|---|---|---|---|---|---|
+ | > | > | < | < | < | > | > |
- | > | > | < | < | < | > | > |
* | > | > | > | > | < | > | > |
/ | > | > | > | > | < | > | > |
( | < | < | < | < | < | = | |
) | > | > | > | > | > | > | |
# | < | < | < | < | < | = |
为实现算符优先算法,可以使用两个工作栈。一个称做 OPTR,用以寄存运算符;另一个称做 OPND ,用以寄存操作数或运算结果。
算法基本思想:
(1)首先置操做数栈为空栈,表达式起始符 “#” 为运算符栈的栈底元素。
(2)依次读入表达式中的每个字符,若是操作数则进 OPND 栈,若是运算符则和 OPTR 栈的栈顶运算符比较优先权后作相应操作,直到整个表达式求值完毕(即 OPTR 栈的栈顶元素和当前读入的字符均为 “#”)。
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
typedef char SElemType; /* 定义栈元素的类型 */
#include<malloc.h> /* malloc()等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<process.h> /* exit() */
/* 函数结果状态代码 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
/* -------------------------- 栈的顺序存储表示 -----------------------------*/
#define STACK_INIT_SIZE 10 /* 存储空间初始分配量 */
#define STACKINCREMENT 2 /* 存储空间分配增量 */
typedef struct SqStack
{
SElemType *base; /* 在栈构造之前和销毁之后,base的值为NULL */
SElemType *top; /* 栈顶指针 */
int stacksize; /* 当前已分配的存储空间,以元素为单位 */
}SqStack; /* 顺序栈 */
/* ----------------------------------------------------------------------------*/
Status visit(SElemType c)
{
printf("%d ", c);
return OK;
}
/* --------------------------------- 需要用到的顺序栈基本操作 ------------------------------------*/
Status InitStack(SqStack *S)
{ /* 构造一个空栈S */
(*S).base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if (!(*S).base)
exit(OVERFLOW); /* 存储分配失败 */
(*S).top = (*S).base;
(*S).stacksize = STACK_INIT_SIZE;
return OK;
}
Status GetTop(SqStack S, SElemType *e)
{ /* 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR */
if (S.top > S.base)
{
*e = *(S.top - 1);
return OK;
}
else
return ERROR;
}
Status Push(SqStack *S, SElemType e)
{ /* 插入元素e为新的栈顶元素 */
if ((*S).top - (*S).base >= (*S).stacksize) /* 栈满,追加存储空间 */
{
(*S).base = (SElemType *)realloc((*S).base, ((*S).stacksize + STACKINCREMENT) * sizeof(SElemType));
if (!(*S).base)
exit(OVERFLOW); /* 存储分配失败 */
(*S).top = (*S).base + (*S).stacksize;
(*S).stacksize += STACKINCREMENT;
}
*((*S).top)++ = e;
return OK;
}
Status Pop(SqStack *S, SElemType *e)
{ /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
if ((*S).top == (*S).base)
return ERROR;
*e = *--(*S).top;
return OK;
}
/* -------------------------------------------------------------------------------------------------*/
/* 表达式求值(输入、输出和中间结果均只能是0~9) */
SElemType Precede(SElemType t1, SElemType t2)
{ /* 根据教科书表3.1,判断两符号的优先关系 */
SElemType f;
switch (t2)
{
case '+':
case '-':if (t1 == '(' || t1 == '#')
f = '<';
else
f = '>';
break;
case '*':
case '/':if (t1 == '*' || t1 == '/' || t1 == ')')
f = '>';
else
f = '<';
break;
case '(':if (t1 == ')')
{
printf("ERROR1\n");
exit(ERROR);
}
else
f = '<';
break;
case ')':switch (t1)
{
case '(':f = '=';
break;
case '#':printf("ERROR2\n");
exit(ERROR);
default: f = '>';
}
break;
case '#':switch (t1)
{
case '#':f = '=';
break;
case '(':printf("ERROR3\n");
exit(ERROR);
default: f = '>';
}
}
return f;
}
Status In(SElemType c)
{ /* 判断c是否为运算符 */
switch (c)
{
case'+':
case'-':
case'*':
case'/':
case'(':
case')':
case'#':return TRUE;
default:return FALSE;
}
}
SElemType Operate(SElemType a, SElemType theta, SElemType b)
{
switch (theta)
{
case'+':
return a + b;
case'-':
return a - b;
case'*':
return a * b;
}
return a / b;
}
SElemType EvaluateExpression() /* 算法3.4 */
{ /* 算术表达式求值的算符优先算法。设OPTR和OPND分别为运算符栈和运算数栈 */
SqStack OPTR, OPND;
SElemType a, b, c, x, theta;
InitStack(&OPTR);
Push(&OPTR, '#');
InitStack(&OPND);
c = getchar();
GetTop(OPTR, &x);
while (c != '#' || x != '#')
{
if (In(c)) /* 是7种运算符之一 */
switch (Precede(x, c))
{
case'<':Push(&OPTR, c); /* 栈顶元素优先权低 */
c = getchar();
break;
case'=':Pop(&OPTR, &x); /* 脱括号并接收下一字符 */
c = getchar();
break;
case'>':Pop(&OPTR, &theta); /* 退栈并将运算结果入栈 */
Pop(&OPND, &b);
Pop(&OPND, &a);
Push(&OPND, Operate(a, theta, b));
break;
}
else if (c >= '0'&&c <= '9') /* c是操作数 */
{
Push(&OPND, c-48);
c = getchar();
}
else /* c是非法字符 */
{
printf("ERROR4\n");
exit(ERROR);
}
GetTop(OPTR, &x);
}
GetTop(OPND, &x);
return x;
}
void main()
{
printf("请输入算术表达式(中间值及最终结果要在0~9之间),并以#结束\n");
printf("%d\n", EvaluateExpression());
}
运行结果:
下面的程序做了些改进,把连续输入的几个数值型字符作为一个整数处理,使数值范围扩大为整数,更具有
实用性。
而且原本课本上以 “#” 表示算式输入结束,以下改为 “=” ,这样更加贴合实际。
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
typedef int SElemType; /* 栈元素类型为整型,改进算法3.4 */; /* 定义栈元素的类型 */
#include<malloc.h> /* malloc()等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<process.h> /* exit() */
/* 函数结果状态代码 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
/* -------------------------- 栈的顺序存储表示 -----------------------------*/
#define STACK_INIT_SIZE 10 /* 存储空间初始分配量 */
#define STACKINCREMENT 2 /* 存储空间分配增量 */
typedef struct SqStack
{
SElemType *base; /* 在栈构造之前和销毁之后,base的值为NULL */
SElemType *top; /* 栈顶指针 */
int stacksize; /* 当前已分配的存储空间,以元素为单位 */
}SqStack; /* 顺序栈 */
/* ----------------------------------------------------------------------------*/
Status visit(SElemType c)
{
printf("%d ", c);
return OK;
}
/* --------------------------------- 需要用到的顺序栈基本操作 ------------------------------------*/
Status InitStack(SqStack *S)
{ /* 构造一个空栈S */
(*S).base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if (!(*S).base)
exit(OVERFLOW); /* 存储分配失败 */
(*S).top = (*S).base;
(*S).stacksize = STACK_INIT_SIZE;
return OK;
}
Status GetTop(SqStack S, SElemType *e)
{ /* 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR */
if (S.top > S.base)
{
*e = *(S.top - 1);
return OK;
}
else
return ERROR;
}
Status Push(SqStack *S, SElemType e)
{ /* 插入元素e为新的栈顶元素 */
if ((*S).top - (*S).base >= (*S).stacksize) /* 栈满,追加存储空间 */
{
(*S).base = (SElemType *)realloc((*S).base, ((*S).stacksize + STACKINCREMENT) * sizeof(SElemType));
if (!(*S).base)
exit(OVERFLOW); /* 存储分配失败 */
(*S).top = (*S).base + (*S).stacksize;
(*S).stacksize += STACKINCREMENT;
}
*((*S).top)++ = e;
return OK;
}
Status Pop(SqStack *S, SElemType *e)
{ /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
if ((*S).top == (*S).base)
return ERROR;
*e = *--(*S).top;
return OK;
}
/* -------------------------------------------------------------------------------------------------*/
SElemType Precede(SElemType t1, SElemType t2) /* 同上 */
{ /* 根据教科书表3.1,判断两符号的优先关系 */
SElemType f;
switch (t2)
{
case '+':
case '-':if (t1 == '(' || t1 == '=')
f = '<';
else
f = '>';
break;
case '*':
case '/':if (t1 == '*' || t1 == '/' || t1 == ')')
f = '>';
else
f = '<';
break;
case '(':if (t1 == ')')
{
printf("ERROR1\n");
exit(ERROR);
}
else
f = '<';
break;
case ')':switch (t1)
{
case '(':f = '=';
break;
case '=':printf("ERROR2\n");
exit(ERROR);
default: f = '>';
}
break;
case '=':switch (t1)
{
case '=':f = '=';
break;
case '(':printf("ERROR2\n");
exit(ERROR);
default: f = '>';
}
}
return f;
}
Status In(SElemType c) /* 几乎与上相同 */
{ /* 判断c是否为运算符 */
switch (c)
{
case'+':
case'-':
case'*':
case'/':
case'(':
case')':
case'=':return TRUE; /* 此句不同于上 */
default:return FALSE;
}
}
SElemType Operate(SElemType a, SElemType theta, SElemType b) /* 有改动 */
{
SElemType c;
switch (theta)
{
case'+':c = a + b;
break;
case'-':c = a - b;
break;
case'*':c = a * b;
break;
case'/':c = a / b;
}
return c;
}
SElemType EvaluateExpression() /* 有改动 */
{ /* 算术表达式求值的算符优先算法。设OPTR和OPND分别为运算符栈和运算数栈 */
SqStack OPTR, OPND;
SElemType a, b, d, x, theta;
char c; /* 存放由键盘接收的字符串 */
char z[6]; /* 存放整数字符串 */
int i;
InitStack(&OPTR); /* 初始化运算符栈 */
Push(&OPTR, '='); /* =是表达式结束标志 */
InitStack(&OPND); /* 初始化运算数栈 */
c = getchar();
GetTop(OPTR, &x);
while (c != '=' || x != '=')
{
if (In(c)) /* 是7种运算符之一 */
switch (Precede(x, c))
{
case'<':Push(&OPTR, c); /* 栈顶元素优先权低 */
c = getchar();
break;
case'=':Pop(&OPTR, &x); /* 脱括号并接收下一字符 */
c = getchar();
break;
case'>':Pop(&OPTR, &theta); /* 退栈并将运算结果入栈 */
Pop(&OPND, &b);
Pop(&OPND, &a);
Push(&OPND, Operate(a, theta, b));
}
else if (c >= '0'&&c <= '9') /* c是操作数 */
{
i = 0;
do
{
z[i] = c;
i++;
c = getchar();
} while (c >= '0'&&c <= '9');
z[i] = 0;
d = atoi(z); /* 将字符串数组转为整型存于d */
Push(&OPND, d);
}
else /* c是非法字符 */
{
printf("ERROR3\n");
exit(ERROR);
}
GetTop(OPTR, &x);
}
GetTop(OPND, &x);
return x;
}
void main() /* 有改动 */
{
printf("请输入算术表达式,负数要用(0-正数)表示,并以=结束\n");
printf("%d\n", EvaluateExpression());
}
运行结果: