最近在学习表达式求值问题,想使用C++或C语言实现一个带圆括号的十进制正整数的表达式求值控制台程序。这个问题可以通过栈或者二叉树遍历来解决。记得以前在学校学习数据结构中栈的应用时看到过,另外编译原理这门课也有讲过。重新翻开<<数据结构-C语言描述 耿国华 主编>>一书的P80~P83第3张有关栈相应的章节时,有一个无括号算术表达式的求值问题,其次在对应的光盘上课程设计里头有表达式求值的相关描述,这里记录如下:
[问题描述]
一个算术表达式是由操作数(operand)、运算符(operator)和界限符(delimiter)组成的。假设操作数是正整数,运算符只含加减乘除等四种运算符,界限符有左右括号和表达式起始、结束符“#”,如:#(7+15)*(23-28/4)#。引入表达式起始、结束符是为了方便。编程利用“算符优先法”求算术表达式的值。
[基本要求]
(1) 从键盘读入一个合法的算术表达式,输出正确的结果。
(2) 显示输入序列和栈的变化过程。
[选作内容]
(1) 扩充运算符集合。
(2) 引入变量操作数。
(3) 操作数类型扩充到实数
相应的C语言代码如下:
//expression.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <conio.h>
#define TRUE 1
#define FALSE 0
#define Stack_Size 50
char ops[7]={'+','-','*','/','(',')','#'}; /*运算符数组*/
int cmp[7][7]={{2,2,1,1,1,2,2}, /*用来进行比较运算符优先级的矩阵,3代表'=',2代表'>',1代表'<',0代表不可比*/
{2,2,1,1,1,2,2},
{2,2,2,2,1,2,2},
{2,2,2,2,1,2,2},
{1,1,1,1,1,3,0},
{2,2,2,2,0,2,2},
{1,1,1,1,1,0,3}};
typedef struct
{
char elem[Stack_Size];
int top;
}SeqStack; /*运算符栈的定义*/
typedef struct
{
int elem[Stack_Size];
int top;
}nSeqStack; /* 运算数栈的定义*/
void InitStack(SeqStack *S) /*初始化运算符栈*/
{
S->top =-1;
}
void InitStackn(nSeqStack *S) /*初始化运算数栈*/
{
S->top =-1;
}
int IsEmpty(SeqStack *S) /*判断栈S为空栈时返回值为真,反之为假*/
{
return(S->top==-1?TRUE:FALSE);
}
int IsEmptyn(nSeqStack *S) /*判断栈S为空栈时返回值为真,反之为假*/
{
return(S->top==-1?TRUE:FALSE);
}
/*判栈满*/
int IsFull(SeqStack *S) /*判断栈S为满栈时返回值为真,反之为假*/
{
return(S->top==Stack_Size-1?TRUE:FALSE);
}
int IsFulln(nSeqStack *S) /*判断栈S为满栈时返回值为真,反之为假*/
{
return(S->top==Stack_Size-1?TRUE:FALSE);
}
int Push(SeqStack *S, char x) /*运算符栈入栈函数*/
{
if (S->top==Stack_Size-1)
{
printf("Stack is full!\n");
return FALSE;
}
else
{
S->top++;
S->elem[S->top]=x;
return TRUE;
}
}
int Pushn(nSeqStack *S, int x) /*运算数栈入栈函数*/
{
if (S->top==Stack_Size-1)
{
printf("Stack is full!\n");
return FALSE;
}
else
{
S->top++;
S->elem[S->top]=x;
return TRUE;
}
}
int Pop(SeqStack *S, char *x) /*运算符栈出栈函数*/
{
if (S->top==-1)
{
printf("运算符栈空!\n");
return FALSE;
}
else
{
*x=S->elem[S->top];
S->top--;
return TRUE;
}
}
int Popn(nSeqStack *S, int *x) /*运算数栈出栈函数*/
{
if (S->top==-1)
{
printf("运算符栈空!\n");
return FALSE;
}
else
{
*x=S->elem[S->top];
S->top--;
return TRUE;
}
}
char GetTop(SeqStack *S) /*运算符栈取栈顶元素函数*/
{
if (S->top ==-1)
{
printf("运算符栈为空!\n");
return FALSE;
}
else
{
return (S->elem[S->top]);
}
}
int GetTopn(nSeqStack *S) /*运算数栈取栈顶元素函数*/
{
if (S->top ==-1)
{
printf("运算符栈为空!\n");
return FALSE;
}
else
{
return (S->elem[S->top]);
}
}
int Isoperator(char ch) /*判断输入字符是否为运算符函数,是返回TRUE,不是返回FALSE*/
{
int i;
for (i=0;i<7;i++)
{
if(ch==ops[i])
return TRUE;
}
return FALSE;
}
/*
int isvariable(char ch)
{ if (ch>='a'&&ch<='z')
return true;
else
return false;
}*/
char Compare(char ch1, char ch2) /*比较运算符优先级函数*/
{
int i,m,n;
char pri; /*保存优先级比较后的结果'>'、'<'、'='*/
int priority; /*优先级比较矩阵中的结果*/
for(i=0;i<7;i++) /*找到相比较的两个运算符在比较矩阵里的相对位置*/
{
if(ch1==ops[i])
m=i;
if (ch2==ops[i])
n=i;
}
priority = cmp[m][n];
switch(priority)
{
case 1:
pri='<';
break;
case 2:
pri='>';
break;
case 3:
pri='=';
break;
case 0:
pri='$';
printf("表达式错误!\n");
break;
}
return pri;
}
int Execute(int a, char op, int b) /*运算函数*/
{
int result;
switch(op)
{
case '+':
result=a+b;
break;
case '-':
result=a-b;
break;
case '*':
result=a*b;
break;
case '/':
result=a/b;
break;
}
return result;
}
int ExpEvaluation()
/*读入一个简单算术表达式并计算其值。optr和operand分别为运算符栈和运算数栈,OPS为运算符集合*/
{
int a,b,v,temp;
char ch,op;
char *str;
int i=0;
SeqStack optr; /*运算符栈*/
nSeqStack operand; /*运算数栈*/
InitStack(&optr);
InitStackn(&operand);
Push(&optr,'#');
printf("请输入表达式(以#结束):\n"); /*表达式输入*/
str =(char *)malloc(50*sizeof(char));
gets(str); /*取得一行表达式至字符串中*/
ch=str[i];
i++;
while(ch!='#'||GetTop(&optr)!='#')
{
if(!Isoperator(ch))
{
temp=ch-'0'; /*将字符转换为十进制数*/
ch=str[i];
i++;
while(!Isoperator(ch))
{
temp=temp*10 + ch-'0'; /*将逐个读入运算数的各位转化为十进制数*/
ch=str[i];
i++;
}
Pushn(&operand,temp);
}
else
{
switch(Compare(GetTop(&optr),ch))
{
case '<':
Push(&optr,ch);
ch=str[i];
i++;
break;
case '=':
Pop(&optr,&op);
ch=str[i];
i++;
break;
case '>':
Pop(&optr,&op);
Popn(&operand,&b);
Popn(&operand,&a);
v=Execute(a,op,b); /* 对a和b进行op运算 */
Pushn(&operand,v);
break;
}
}
}
v=GetTopn(&operand);
return v;
}
void main() /*主函数*/
{
int result;
result=ExpEvaluation();
printf("\n表达式结果是%d\n",result);
}
这其中有几个难点:
1、运算符优先级矩阵的设计,里面的运算符优先级如何比较,里面我觉得涉及到编译原理的知识。
2、使用栈求表达式的值
在开源中国的代码分享中看到了一篇用栈实现表达式求值的一篇文章,作者网名为路伟,网址为:http://www.oschina.net/code/snippet_818195_15722,现在引述如下,算作借鉴吧。
作者用栈ADT实现了,表达式求值问题。
用户输入表达式以字符串形式接收,然后处理计算最后求出值
目前仅支持运算符 '(' , ')' , '+' , '-', '*' , '/' 的表达式求值。
内附源代码和一个可执行程序,无图形界面
代码如下:
//ElemType.h
/***
*ElemType.h - ElemType的定义
*
****/
#ifndef ELEMTYPE_H
#define ELEMTYPE_H
// 定义包含int和float的共同体
typedef union ET{
float fElem;
char cElem;
}ET;
typedef ET ElemType;
//int compare(ElemType x, ElemType y);
void visit(ElemType e);
#endif /* ELEMTYPE_H */
//mainTest.cpp
/**
*
* 文件名称:mainTest.cpp
* 摘 要:利用 ADT-栈 完成表达式求值,表达式求值相关函数定义
**/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "DynaSeqStack.h"
// 表达式的最大长度
#define MAX_EXPRESSION_LENGTH 100
// 操作数的最大值
#define MAX_OPERAND_LENGTH 50
// 查找字符在串中第一次出现位置
int strpos(const char *str, const char ch)
{
int i;
for(i = 0; i < strlen(str); i++)
{
if(str[i] == ch)
break;
}
return i;
}
// 判断运算符优先级
int operPrior(const char oper)
{
char operList[] = {'-', '+', '*', '/', '(', ')', ''}; // 6种运算符
return strpos(operList, oper) / 2;
}
// 计算求值
bool calc(float a, float b, char oper, float *result)
{
switch(oper)
{
case '+':
*result = a + b;
break;
case '-':
*result = a - b;
break;
case '*':
*result = a * b;
break;
case '/':
if(b != 0)
*result = a / b;
else
return false;
break;
default:
*result = 0;
return false;
}
return true;
}
// 表达式求值计算处理函数
bool calcExpression(const char *exp, float *result)
{
SqStack ope; // 运算符栈
SqStack num; // 操作数栈
ET buf; // 暂存入栈、出栈元素
unsigned short pos = 0; // 表达式下标
char operand[MAX_OPERAND_LENGTH] = {0}; // 操作数
char operList[] = {'-', '+', '*', '/', '(', ')', ''}; // 6种运算符
// 初始化栈
InitStack(&ope);
InitStack(&num);
buf.cElem = '#';
if(false == Push(&ope, buf))
return false;
// 字符串处理,入栈,计算求值
while(strlen(exp) > pos)
{
// ASCII 48~57 为数字 .(小数点) 为 46
if(exp[pos] >= 48 && exp[pos] <= 57 || exp[pos] == 46) //若扫描的字符为数字0~9或者小数点'.'
{
// 下标从pos开始,长度为len的字符串为操作数
int len = 1;
// 求数字字符长度len
while(exp[pos + len] >= 48 && exp[pos + len] <= 57 || exp[pos + len] == 46)
{
len++;
}
strncpy(operand, (exp + pos), len);
operand[len] = '';
// 将字符串操作数转化为浮点数
buf.fElem = atof(operand);
// 入栈
if(false == Push(&num, buf))
return false;
// 修改pos指示位置
pos += len;
}
else if(strchr(operList, exp[pos]) != NULL) // 为运算符
{
GetTop(ope, &buf);
if('#' == buf.cElem || ( '(' == buf.cElem && exp[pos] != ')' ))
{
buf.cElem = exp[pos];
if(false == Push(&ope, buf))
return false;
}
else
{
// 比较运算符的优先级
if(operPrior(buf.cElem) < operPrior(exp[pos]) && exp[pos] != ')')
{
// 运算符入栈
buf.cElem = exp[pos];
if(false == Push(&ope, buf))
return false;
}
else
{
GetTop(ope, &buf);
if(buf.cElem == '(' && exp[pos] == ')')
{
if(false == Pop(&ope, &buf))
return false;
}
else
{
float num1, num2, res;
char op;
// 取运算符
if(false == Pop(&ope, &buf))
return false;
op = buf.cElem;
// 取第一个操作数
if(false == Pop(&num, &buf))
return false;
num1 = buf.fElem;
// 取第二个操作数
if(false == Pop(&num, &buf))
return false;
num2 = buf.fElem;
// 调用计算函数
if(false == calc(num2, num1, op, &res))
return false;
// 结果入栈
buf.fElem = res;
if(false == Push(&num, buf))
return false;
// pos回溯,下一轮再次处理此运算符,因为此次未处理(只运算符出栈,计算)
pos--;
}
}
}// else
// 处理完运算符,指示器加1
pos++;
}
else // 为非法字符
{
return false;
}
}// while
// 表达式遍历完一遍后, 运算符出栈,依次计算即可
while(true)
{
GetTop(ope, &buf);
if(buf.cElem == '#')
break;
float num1, num2, res;
char op;
// 取运算符
if(false == Pop(&ope, &buf))
return false;
op = buf.cElem;
// 取第一个操作数
if(false == Pop(&num, &buf))
return false;
num1 = buf.fElem;
// 取第二个操作数
if(false == Pop(&num, &buf))
return false;
num2 = buf.fElem;
// 调用计算函数
if(false == calc(num2, num1, op, &res))
return false;
// 结果入栈
buf.fElem = res;
if(false == Push(&num, buf))
return false;
}
if(1 == StackLength(num))
{
if(false == Pop(&num, &buf))
return false;
*result = buf.fElem;
}
else
{
return false;
}
// 销毁栈
DestroyStack(&ope);
DestroyStack(&num);
return true;
}// calcExpression
// 初始化程序的界面提示信息
void initTip(void)
{
printf("表达式求值模拟程序n");
printf("n功能菜单:n");
printf("=====================n");
printf("[1]输入表达式求值n[0]退出n");
printf("=====================n");
}// initTip
int main(void)
{
int selectNum = 0; // 记录选择的命令
char exp[MAX_EXPRESSION_LENGTH] = {0}; // 表达式
float result; // 存储结果
// 初始化提示信息
initTip();
do{
printf("请输入你的选择 (0~1):");
scanf("%d", &selectNum);
switch(selectNum)
{
case 0:
break;
case 1:
// input expression
printf("请输入表达式:");
scanf("%s", exp);
fflush(stdin);
// 调用表达式求值函数
if(false == calcExpression(exp, &result))
{
printf("表达式错误!nn");
continue;
}
else
{
// 输出运算结果
printf("计算结果如下:n%s = %.3fnn", exp, result);
}
break;
default:
printf("选择非法! 请重新选择。nn");
fflush(stdin);
continue;
}// switch
}while(selectNum != 0);
return 0;
}
//DynaSeqStack.h
/***
*DynaSeqStack.h - 动态顺序栈的定义
*
****/
#if !defined(DYNASEQSTACK_H)
#define DYNASEQSTACK_H
#include "ElemType.h"
/*------------------------------------------------------------
// 栈结构的定义
------------------------------------------------------------*/
typedef struct Stack
{
ElemType *base; // 栈基址
ElemType *top; // 栈顶
int stacksize; // 栈存储空间的尺寸
} SqStack;
/*------------------------------------------------------------
// 栈的基本操作
------------------------------------------------------------*/
bool InitStack(SqStack *S);
void DestroyStack(SqStack *S);
bool StackEmpty(SqStack S);
int StackLength(SqStack S);
bool GetTop(SqStack S, ElemType *e);
void StackTraverse(SqStack S, void (*fp)(ElemType));
bool Push(SqStack *S, ElemType e);
bool Pop(SqStack *S, ElemType *e);
#endif /* DYNASEQSTACK_H */
//DynaSeqStack.cpp
/***
*DynaSeqStack.cpp - 动态顺序栈,即栈的动态顺序存储实现
*
*
*说明:栈的动态顺序存储实现
*
*
****/
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <assert.h>
#include "DynaSeqStack.h"
const int STACK_INIT_SIZE = 100; // 初始分配的长度
const int STACKINCREMENT = 10; // 分配内存的增量
/*------------------------------------------------------------
操作目的: 初始化栈
初始条件: 无
操作结果: 构造一个空的栈
函数参数:
SqStack *S 待初始化的栈
返回值:
bool 操作是否成功
------------------------------------------------------------*/
bool InitStack(SqStack *S)
{
S->base = (ElemType *)malloc(sizeof(ElemType) * STACK_INIT_SIZE);
if(NULL == S->base)
return false;
S->top = S->base;
S->stacksize = STACK_INIT_SIZE;
return true;
}
/*------------------------------------------------------------
操作目的: 销毁栈
初始条件: 栈S已存在
操作结果: 销毁栈S
函数参数:
SqStack *S 待销毁的栈
返回值:
无
------------------------------------------------------------*/
void DestroyStack(SqStack *S)
{
if(S != NULL)
{
free(S->base);
S = NULL; // 防止野指针
}
}
/*------------------------------------------------------------
操作目的: 判断栈是否为空
初始条件: 栈S已存在
操作结果: 若S为空栈,则返回true,否则返回false
函数参数:
SqStack S 待判断的栈
返回值:
bool 是否为空
------------------------------------------------------------*/
bool StackEmpty(SqStack S)
{
return (S.base == S.top);
}
/*------------------------------------------------------------
操作目的: 得到栈的长度
初始条件: 栈S已存在
操作结果: 返回S中数据元素的个数
函数参数:
SqStack S 栈S
返回值:
int 数据元素的个数
------------------------------------------------------------*/
int StackLength(SqStack S)
{
return (S.top - S.base);
}
/*------------------------------------------------------------
操作目的: 得到栈顶元素
初始条件: 栈S已存在
操作结果: 用e返回栈顶元素
函数参数:
SqStack S 栈S
ElemType *e 栈顶元素的值
返回值:
bool 操作是否成功
------------------------------------------------------------*/
bool GetTop(SqStack S, ElemType *e)
{
if(NULL == e)
return false;
*e = *(S.top - 1);
return true;
}
/*------------------------------------------------------------
操作目的: 遍历栈
初始条件: 栈S已存在
操作结果: 依次对S的每个元素调用函数fp
函数参数:
SqStack S 栈S
void (*fp)() 访问每个数据元素的函数指针
返回值:
无
------------------------------------------------------------*/
void StackTraverse(SqStack S, void (*fp)(ElemType))
{
if(NULL == fp)
return;
for(int i = 0; i < (S.top - S.base); i++)
{
fp( *(S.base + i) );
}
}
/*------------------------------------------------------------
操作目的: 压栈——插入元素e为新的栈顶元素
初始条件: 栈S已存在
操作结果: 插入数据元素e作为新的栈顶
函数参数:
SqStack *S 栈S
ElemType e 待插入的数据元素
返回值:
bool 操作是否成功
------------------------------------------------------------*/
bool Push(SqStack *S, ElemType e)
{
if( NULL == S ) // S为空
return false;
if((S->top - S->base) == S->stacksize) // 栈满
{
// 重新分配内存
S->base = (ElemType *)realloc(S->base, sizeof(ElemType) * (S->stacksize + STACKINCREMENT));
//修改top
S->top = S->base + S->stacksize;
//修改size
S->stacksize += STACKINCREMENT;
}
*(S->top) = e;
S->top++;
return true;
}
/*------------------------------------------------------------
操作目的: 弹栈——删除栈顶元素
初始条件: 栈S已存在且非空
操作结果: 删除S的栈顶元素,并用e返回其值
函数参数:
SqStack *S 栈S
ElemType *e 被删除的数据元素值
返回值:
bool 操作是否成功
------------------------------------------------------------*/
bool Pop(SqStack *S, ElemType *e)
{
if(NULL == S || NULL == e || S->top == S->base) // S为空 或 e为空 或 栈空
return false;
S->top--;
*e = *(S->top);
return true;
}
作者使用C语言中的共用体解决了栈的复用,有点类似于C++里面的模版类,这钟做法避免了因为不同的元素类型重复建立栈的数据结构,因为运算数栈和运算符栈的元素类型是不同的,一个为int、float、double类型,另一个为char型,当然如果使用C++写一个栈的类模版或者直接使用STL的stack就没这么麻烦了。