这次的题是在学校课程上机实验时安排的任务,以下是几个关键步骤。
目录
问题描述
具体算法
实现优先级比较函数
整体代码
运行结果?
结语
问题描述
设计一个程序,演示用算符优先法对算术表达式求值的过程。利用算符优先关系,实现对算术四则混合运算表达式的求值。
(1)输入的形式:表达式,例如2*(3+4)#?
??????????包含的运算符只能有'+' 、'-' 、'*' 、'/' 、'('、 ')',“#”代表输入结束符;
(2)输出的形式:运算结果,例如2*(3+4)=14; (3)程序所能达到的功能:对表达式求值并输出。
借鉴严蔚敏编的《数据结构》书上提的思路,咱们可以设置两个工作栈:用于存储运算符的栈opter,以及用于存储操作数及中间结果的栈opnd。
具体算法
(1)首先将操作数栈opnd设为空栈,而将'#'作为运算符栈opter的栈底元素,这样的目的是判断表达式是否求值完毕。
(2)依次读入表达式的每个字,表达式须以'#'结,读入字符若是操作数则入栈opnd,读入字符若是运算符,则将此运算符c与opter的栈顶元素top比较优先级后执行相应的操作,操作如下:?
- (i)若top的优先级小于c,即top<c,则将c直接入栈opter,并读入下一字符赋值给c;
- (ii)若top的优先级等于c,即top=c,则弹出opter的栈顶元素,并读入下一字符赋值给c,这一步目的是进行括号操作;
- (iii)若top优先级高于c,即top>c,则表明可以计算,此时弹出opnd的栈顶两个元素,并且弹出opter栈顶的的运算符,计算后将结果放入栈opnd中。直至opter的栈顶元素和当前读入的字符均为'#',此时求值结束。
?根据运算符的优先关系:
实现优先级比较函数
? 将其代码实现为:
int getIndex(char theta) //获取theta所对应的索引
{
int index = 0;
switch (theta)
{
case '+':
index = 0;
break;
case '-':
index = 1;
break;
case '*':
index = 2;
break;
case '/':
index = 3;
break;
case '(':
index = 4;
break;
case ')':
index = 5;
break;
case '#':
index = 6;
default:break;
}
return index;
}
char getPriority(char theta1, char theta2) //获取theta1与theta2之间的优先级
{
const char priority[][7] = //算符间的优先级关系
{
{ '>','>','<','<','<','>','>' },
{ '>','>','<','<','<','>','>' },
{ '>','>','>','>','<','>','>' },
{ '>','>','>','>','<','>','>' },
{ '<','<','<','<','<','=','0' },
{ '>','>','>','>','0','>','>' },
{ '<','<','<','<','<','0','=' },
};
int index1 = getIndex(theta1);
int index2 = getIndex(theta2);
return priority[index1][index2];
}
有了如上定义,我们只需要向函数getPriority传入opter栈顶元素和表达式中的字符,由函数的返回值即可得到优先级。
但在正式实现代码之前,有一点值得注意的是两个栈opter和opnd需要存入的数据类型(char和int)不一样,因此各栈的相关操作也需要区别性的定义,即Opnd_Stack、Opnd_Push、Opnd_Pop等等函数均是对运算数栈Opnd操作,同理Opter_Stack、Opter_Push、Opter_Pop等等函数
均是对运算符栈Opter操作。
整体代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSIZE 20
#define ERROR 0
#define OK 1
#define Stack_Size 100 // 初始化栈的最大长度
typedef int Status;
typedef struct{
char Elem[Stack_Size];
int top;
}Opter_Stack;
typedef struct{
int Elem[Stack_Size];
int top;
}Opnd_Stack;
void InitOpter(Opter_Stack *S){//初始化运算符栈
S->top=-1;
}
void InitOpnd(Opnd_Stack *S){//初始化操作数栈
S->top=-1;
}
int Opter_Pop(Opter_Stack *S)//弹出运算符栈
{
if(S->top==-1)
{
printf("运算符栈为空\n");
exit(10);
}
S->top--;
return 1;
}
int Opnd_Pop(Opnd_Stack *S)
{
if(S->top==-1)
{
printf("运算符栈为空\n");
exit(11);
}
S->top--;
return 1;
}
int Opter_Push(Opter_Stack* S,char ch)
{
if(S->top==Stack_Size-1)
{
printf("运算符栈满\n");
exit(12);
}
S->top++;
S->Elem[S->top]=ch;
return 1;
}
int Opnd_Push(Opnd_Stack* S,int ch)//入操作数栈
{
if(S->top==Stack_Size-1)
{
printf("运算符栈满\n");
exit(13);
}
S->top++;
S->Elem[S->top]=ch;
return 1;
}
char GetOpter(Opter_Stack *S)//获取运算符栈的栈顶元素,注意区分返回值类型
{
if(S->top==-1)
{
printf("运算符栈为空\n");
exit(17);
}
return S->Elem[S->top];
}
int GetOpnd(Opnd_Stack *S)
{
if(S->top==-1)
{
printf("操作数栈为空\n");
exit(18);
}
return S->Elem[S->top];
}
int getIndex(char theta) //获取theta所对应的索引
{
int index = 0;
switch (theta)
{
case '+':
index = 0;
break;
case '-':
index = 1;
break;
case '*':
index = 2;
break;
case '/':
index = 3;
break;
case '(':
index = 4;
break;
case ')':
index = 5;
break;
case '#':
index = 6;
default:break;
}
return index;
}
char getPriority(char theta1, char theta2) //获取theta1与theta2之间的优先级
{
const char priority[][7] = //算符间的优先级关系
{
{ '>','>','<','<','<','>','>' },
{ '>','>','<','<','<','>','>' },
{ '>','>','>','>','<','>','>' },
{ '>','>','>','>','<','>','>' },
{ '<','<','<','<','<','=','0' },
{ '>','>','>','>','0','>','>' },
{ '<','<','<','<','<','0','=' },
};
int index1 = getIndex(theta1);
int index2 = getIndex(theta2);
return priority[index1][index2];
}
Status IsNumber(char ch){ //判断字符是否为运算数
if(ch>='0'&&ch<='9') return OK;
else return ERROR;
}
int Operate(int a, char op, int b){ //四则运算
switch(op){
case '+': return a+b;
case '-': return a-b;
case '*': return a*b;
case '/': return a/b;
}
}
Status Calculator(char exp[]){
int i=0, x, y;
char opt;
Opnd_Stack *opnd; InitOpnd(opnd);
Opter_Stack *opter; InitOpter(opter); //创建并初始化工作栈opter、opnd
Opter_Push(opter, '#'); //将'#'压入opter栈底作为结束标志
while(exp[i]!='#'||GetOpter(opter)!='#'){
if(IsNumber(exp[i])){
int data[10];
int k=0, j, num=0;/*考虑到运算数可能为多位的问题,num作为一个中间数,
用于将表达式中的为运算数的字符转化为整数然后入栈, k是用于将运算数存入data数组*/
while(IsNumber(exp[i]))
{
data[k] =exp[i] - '0';
k++;
i++;
}
for(j=0;j<k;j++)
{
num = num*10+data[j];
}
Opnd_Push(opnd,num);
}
else{
switch(getPriority(GetOpter(opter), exp[i])){
case '<':Opter_Push(opter,exp[i]); /*若top的优先级小于exp[i],即top<exp[i],则将exp[i]直接入栈opter,并读入下一字符*/
i++;
break;
case '=':Opter_Pop(opter); /*若top的优先级等于exp[i],即top=exp[i],则弹出opter的栈顶元素,并读入下一字符,这一步目的是进行括号操作*/
i++;
break;
case '>':opt=GetOpter(opter); Opter_Pop(opter); /*若top优先级高于exp[i],即top>exp[i],则表明可以计算,此时弹出opnd的栈顶两个元素,
并且弹出opter栈顶的的运算符,计算后将结果放入栈opnd中*/
y=GetOpnd(opnd); Opnd_Pop(opnd);
x=GetOpnd(opnd); Opnd_Pop(opnd);
Opnd_Push(opnd,Operate(x,opt,y));
break;
}
}
}
/*跳出循环说明求值结束,此时opnd栈顶元素即为最终结果*/
return GetOpnd(opnd);
}
int main(){
int ret;
char exp[MAXSIZE], real_exp[MAXSIZE];
printf("Input the expression:\n");
gets(exp);
if(exp[strlen(exp)-1]!='#'){
printf("表达式有误!");
return 0;
}
else{
strcpy(real_exp,exp);
real_exp[strlen(real_exp)-1]='\0';
printf("%s\n", real_exp);
ret=Calculator(exp);
printf("= %d\n", ret);
}
return 0;
}
运行结果?
以下便是调试运行得到的几个结果:
?
?显然运算结果也和我们预期的一样!
结语
但程序不够完善的地方是只实现了整型运算数的计算,对于实型运算数的求值还需得在各个函数间及定义参数时做些微小的改动,迫于主修课程数量陡增的压力,不得先搁置一下,就到这吧。??
|