问题及代码:
/*
Copyright (c)2016,烟台大学计算机与控制工程学院
All rights reserved.
文件名称:ysx.cpp
作 者:于苏显
完成日期:2016年11月12日
版 本 号:v1.0
问题描述:利用sqstack.h中栈的基本运算,实现将一个算术表达式转换为对应的后缀表达式的算法。
例如,输入(56-20)/(4+2)
输出后缀表达式::56#20#-4#2#+/ 和 运算结果:6
要求在数字后加#。
输入描述:算术表达式
程序输出:后缀表达式 和 对后缀表达式求值的结果
*/
利用顺序栈算法库求后缀表达式和运算结果
#include "sqstack.h"
#include <math.h>
int wrong=0; //判断表达式是否正确(全局变量)
struct //定义运算符优先级
{
char ch; //运算符
int pri; //优先级
}
lpri[]= {{'=',0},{'(',1},{'*',5},{'/',5},{'+',3},{'-',3},{')',6}},//左运算符为栈顶运算符
rpri[]= {{'=',0},{'(',6},{'*',4},{'/',4},{'+',2},{'-',2},{')',1}};//右运算符为表达式运算符
//若遇到')'(优先级为1)将'('后入栈的运算符依次出栈
bool Match(char *s) //判断括号是否匹配
{
char c;
int match=1;
SqStack *p;
InitStack(p);
for(int i=0; s[i]!='\0'&&match; i++)
{
if(s[i]=='(') Push(p, s[i]);
else if(s[i]==')')
{
if(GetTop(p,c)) Pop(p,c);
else
{
match=0;
break;
}
}
}
if(StackEmpty(p)&&match==1)
return true;
else
return false;
}
int leftpri(char op) //求左运算符op的优先级
{
int i;
for (i=0; i<7; i++)
if (lpri[i].ch==op)
return lpri[i].pri;
}
int rightpri(char op) //求右运算符op的优先级
{
int i;
for (i=0; i<7; i++)
if (rpri[i].ch==op)
return rpri[i].pri;
}
bool InOp(char ch) //判断是否为运算符
{
if (ch=='(' || ch==')' || ch=='+' || ch=='-' || ch=='*' || ch=='/')
return true;
else
return false;
}
int Precede(char op1,char op2) //比较运算符优先级
{
if (leftpri(op1)==rightpri(op2))
return 0;
else if (leftpri(op1)<rightpri(op2))
return -1;
else
return 1;
}
void trans(char *exp,char *postexp) //将算术表达式exp转换成后缀表达式postexp
{
SqStack *opstack; //定义运算符栈
int i=0,first=0; //i作为postexp的下标
char ch;
InitStack(opstack); //用初始化栈运算为栈分配空间
Push(opstack, '=');
while (*exp!=NULL)
{
if (InOp(*exp)) //运算符
{
if(*exp=='+'||*exp=='-'||*exp=='*'||*exp=='/')
{
if( (*(exp-1)=='+'||*(exp-1)=='-'||*(exp-1)=='*'||*(exp-1)=='/') || *(exp+1)==NULL || first==0)
//若表达式中有两个连续的算术运算符或首位,末位字符为算术运算符,表达式错误
//经测试,当*exp为首位字符时,*(exp-1)='?'(不知道原因)
{
wrong=1;
return;
}
}
if(*exp==')' && *(exp-1)=='(')
{
wrong=1;
return;
}
GetTop(opstack, ch); //取得栈顶的运算符
switch(Precede(ch ,*exp))
{
case -1: //栈顶运算符的优先级低(进栈)
Push(opstack, *exp);
exp++; //继续扫描
first++;
break;
case 0: //只有括号满足这种情况
Pop(opstack, ch); //将(退栈
exp++; //继续扫描
break;
case 1: //退栈并输出到postexp中
postexp[i++]=ch;
Pop(opstack, ch);//出栈后不执行exp++,继续用当前运算符比较
break;
}
}
else if ((*exp>='0' && *exp<='9')||*exp=='.') //数字
{
while ((*exp>='0' && *exp<='9')||*exp=='.')
{
if( (*exp=='.' && *(exp-1)=='.') || (*(exp+1)=='('))//连续两个 '.' 或数字后直接跟'(' 表达式错误
{
wrong=1;
return;
}
postexp[i++]=*exp++;
first++;
}
postexp[i++]='#'; //用#标识一个数值串结束
}
else//出现除了运算符和数字以外的字符,表达式错误
{
wrong=1;
return;
}
}
Pop(opstack, ch);
while (ch!='=') //此时exp扫描完毕,退栈到'='为止
{
postexp[i++]=ch;
Pop(opstack, ch);
}
postexp[i]='\0'; //给postexp表达式添加结束标识
DestroyStack(opstack);
}
double compvalue(char postexp[])
{
struct//定义数值栈
{
float data[MaxSize];
int top;
} st;
int f;
double d;
char ch;
int t=0;
st.top=-1;
ch=postexp[t++];
while (ch)//postexp字符串未扫描完时循环
{
switch (ch)
{
case'+':
st.data[st.top-1]=st.data[st.top-1]+st.data[st.top];
st.top--;
break;
case '-':
st.data[st.top-1]=st.data[st.top-1]-st.data[st.top];
st.top--;
break;
case '*':
st.data[st.top-1]=st.data[st.top-1]*st.data[st.top];
st.top--;
break;
case '/': //未表达st.data[st.top]==0的情形
st.data[st.top-1]=st.data[st.top-1]/st.data[st.top];
st.top--;
break;
default:
d=0; //将数字字符转换成数值存放到d中
f=-1;
while ((ch>='0' && ch<='9')||ch=='.')
{
if(ch!='.') //整数情况
{
d=10*d+ch-'0';
ch=postexp[t++];
}
else //小数情况
{
ch=postexp[t++];//越过小数点,扫描到字符串的下一位
while(ch>='0' && ch<='9')//扫描到小数点后的数字被读取完为止
{
d+=pow(10,f)*(ch-'0');
f--;
ch=postexp[t++];
}
}
}
st.data[++st.top]=d;
}
ch=postexp[t++];
}
return st.data[st.top];
}
int main()
{
char exp[100];
char postexp[200];
printf(" 表达式的转换与求值\n\n");
printf("请输入表达式\n");
scanf("%s",exp);
if(!Match(exp)) wrong=1;
trans(exp,postexp);
if(!wrong)//表达式正确,输出后缀表达式和运算结果
{
printf("中缀表达式: %s\n",exp);
printf("后缀表达式: %s\n",postexp);
printf("表达式的值为: %g\n",compvalue(postexp));
}
else//表达式错误
printf("表达式有误\n");
return 0;
}
运行结果:
知识点总结:使用了顺序栈算法库,括号是否匹配,熟练了后缀表达式的转换和求值方法。
学习心得:在老师给出的原始代码的基础上加了些功能,可以实现 小数的运算,能判断出错误的表达式和错误的书写方式。