第1章 问题描述与分析
1.1问题描述
一个算术表达式是由操作数(operand)、运算符(operator)和界限符(delimiter)组成的。假设操作数是正整数,运算符只含加减乘除等四种运算符,界限符有左右括号和表达式起始、结束符“#”,如:#(7+15)(23-28/4)#。引入表达式起始、结束符是为了方便。编程利用“算符优先法”求算术表达式的值。
从键盘读入一个合法的算术表达式,输出正确的结果;显示输入序列和栈的变化过程,操作数类型扩充到实数。
1.2设计分析
在算术表达式中,运算符位于两个操作数中间的表达式称为中缀表达式,例如1+23就是一个中缀表达式,中缀表达式是一种最常用的表达式形式,日常生活中的表达式一般都是用中缀表达式。对中缀表达式的运算,一般遵循先乘除后加减,从左到右计算,先括号内后括号外的规则,因此中缀表达式不仅要依赖运算符优先级,还要处理括号。算术表达式的另一种形式,后缀表达式或逆波兰表达式,就是在算术表达式中运算符在操作数的后面。1+23的后缀表达式为123+。后缀表达式中已经考虑了运算符的优先级,没有括号,只有操作数和运算符,而且越放在前面的运算符越优先执行。同样在算术表达式中,如果运算符在操作数的前面称为前缀表达式如1+23的前缀表达式为+123。后缀表达式是一种十分有用的表达式,它将复杂表达式转化为可以依靠简单的操作,得到计算结果的表达式,所以对中缀表达式的求值过程,是先将中缀表达式转化成后缀表达式,然后对该后缀表达式求值
第2章 数据结构与算法设计
2.1数据结构设计
所运用到的存储结构定义:
顺序栈结构
typedef struct
{
ElemType data[MaxSize]; //存放栈中的数据元素
int top; //栈顶指针,即存放栈顶元素在data数组中的下标
}SqStack; //顺序栈类型
2.2算法设计
中缀表达式转后缀表达式的函数算法执行步骤如下,
第一步,从exp中读取字符
第二步,判断字符是否为’\0’。若为’\0’,则将Optr中的所有运算符依次出栈并存放到postexp中,转至第八步。若不为’\0’,则继续执行第三步
第三步,判断读取的字符是否为数字,若为数字则依次将所有的数字存放到postexp中,以#结束,转至第一步。若不为数字字符继续执行下一步
第四步,判断是否为操作符,若是则转至下一步。若不是则执行第六步
第五步,判断Optr是否为空或者栈顶元素为’(’,若不是则比较与栈顶元素的优先级,若栈顶元素优先级高,则将栈顶元素出栈,存入postexp中,将该操作符进栈,反之直接将该操作符存入postexp中。若Optr为空或者栈顶元素为’(’,则将该操作符直接进栈。无论哪一种情况,最终转至第一步。
第六步,判断读取的字符是否为’)’,若是,则将栈中元素依次出栈存入postexp中,直到遇到’(’,将其出栈后便停止出栈,转至第一步,若不是则转至下一步
第七步,该元素为‘(’,直接将其进Optr,转至第一步。
第八步,输出postexp,其结果为后缀表达式
后缀表达式求值的函数算法执行步骤如下,
第一步,从postexp中读取字符
第二步,判断读取的字符是否为’\0’,若是则转至第七步,若不是则执行下一步
第三步,判断读取的字符是否为数字字符,若是则将连续的数字串转换为对应的数值并将其进数值栈Opnd,转至第一步。若不是则转至下一步
第四步,该字符是为操作符,判断该操作符为单目运算符,还是双目运算符。若为单目运算符,转至第五步,若为双目运算符,转至第六步。
第五步,将Opnd栈顶元素弹出,进行运算,将运算结果再次压入Opnd栈顶。返回第一步。
第六步,将Opnd栈顶元素与次栈顶元素弹出进行运算,次栈顶对栈顶进行运算。结果再次压入Opnd栈顶。返回第一步。
第七步,返回栈顶元素,该元素则为最终的结果。
附录
(该章将程序的所有代码写入)
#include<iostream>
#include<stdlib.h>
using namespace std;
#include<malloc.h>
#include<math.h>
#define MaxSize 100
typedef char ElemType;
typedef double ElemType1;
typedef struct
{
ElemType data[MaxSize]; //存放栈中的数据元素
int top; //栈顶指针,即存放栈顶元素在data数组中的下标
}SqStack; //顺序栈类型
void InitStack(SqStack * &s) //初始化栈的函数
{
s=(SqStack *)malloc(sizeof(SqStack)); //分配一个顺序栈空间,首地址存放在s中
s->top=-1; //栈顶指针置为-1,表示栈空
}
void DestroyStack(SqStack *&s) //销毁栈的函数
{
free(s);
}
bool StackEmpty(SqStack *s) //判断栈是否为空,该函数实际上用于判断s->top==-1是否成立
{
return(s->top==-1);
}
bool Push(SqStack * &s,ElemType e) //进栈
{
if(s->top==MaxSize-1) //栈满的情况,即栈上溢出
return false;
s->top++; //栈顶指针增1
s->data[s->top]=e; //元素e放在栈顶指针处
return true;
}
bool Pop(SqStack * &s,ElemType &e) //出栈
{
if(s->top==-1) //栈为空的情况,即栈下溢出
return false;
e=s->data[s->top]; //取栈顶元素
s->top--; //栈顶指针减1
return true;
}
bool GetTop(SqStack *s,ElemType &e) //取栈顶元素
{
if(s->top==-1) //栈为空的情况,即栈下溢出
return false;
e=s->data[s->top]; //取栈顶元素
return true;
}
typedef struct
{
ElemType1 data[MaxSize]; //存放栈中的数据元素
int top; //栈顶指针,即存放栈顶元素在data数组中的下标
}SqStack1; //顺序栈类型
void InitStack(SqStack1 * &s) //初始化栈的函数
{
s=(SqStack1 *)malloc(sizeof(SqStack1)); //分配一个顺序栈空间,首地址存放在s中
s->top=-1; //栈顶指针置为-1,表示栈空
}
void DestroyStack1(SqStack1 *&s) //销毁栈的函数
{
free(s);
}
bool StackEmpty(SqStack1 *s) //判断栈是否为空,该函数实际上用于判断s->top==-1是否成立
{
return(s->top==-1);
}
bool Push1(SqStack1 * &s,ElemType1 e) //进栈
{
if(s->top==MaxSize-1) //栈满的情况,即栈上溢出
return false;
s->top++; //栈顶指针增1
s->data[s->top]=e; //元素e放在栈顶指针处
return true;
}
bool Pop1(SqStack1 * &s,ElemType1 &e) //出栈
{
if(s->top==-1) //栈为空的情况,即栈下溢出
return false;
e=s->data[s->top]; //取栈顶元素
s->top--; //栈顶指针减1
return true;
}
bool GetTop1(SqStack1 *s,ElemType1 &e) //取栈顶元素
{
if(s->top==-1) //栈为空的情况,即栈下溢出
return false;
e=s->data[s->top]; //取栈顶元素
return true;
}
void trans(char *exp,char postexp[]) //将算术表达式exp转化成后缀表达式postexp
{
char e;
int i=0;
SqStack*Optr; //定义运算符栈指针
InitStack(Optr); //初始化运算符栈
while( *exp!='\0') //exp表达式未扫描完时循环
{
switch(*exp)
{
case '('://判定左括号
exp++;
if(*exp=='-')
{
exp--;
Push(Optr,'('); //左括号进栈
exp+=2;
postexp[i++]='@';
break;
}
exp--;
Push(Optr,'(');
exp++; //继续扫描其他字符
break;
case ')': //判定右括号
Pop(Optr,e); //出栈元素e
while(e!='(') //不为(时循环
{
postexp[i++]=e; //将e存放到postexe中
Pop(Optr,e); //出栈元素e
}
exp++; //继续扫描其他字符
break;
case '+': //判定为+或-号
case '-':
while(!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr,e); //取栈顶元素e
if(e!='(') //e不是'('
{
postexp[i++]=e; //将e存放到postexp中
Pop(Optr,e); //出栈元素e
}
else
break; //e是'('时退出循环
}
Push(Optr,*exp); //将'+'或'-'进栈
exp++; //继续扫描其他字符
break;
case '*': //判定为'*'或'/'号
case '/':
while(!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr,e); //取栈顶元素e
if(e=='*'||e=='/') //e是'*'或'/'
{
postexp[i++]=e; //将e存放到postexp中
Pop(Optr,e); //出栈元素e
}
else
break; //e是非'*'或'/'时退出循环
}
Push(Optr,*exp); //将'*'或'/'进栈
exp++; //继续扫描其他字符
break;
case '^': //判断为^号
while(!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr,e); //取栈顶元素e
if(e=='^') //e是'^'
{
postexp[i++]=e; //将e存放到postexp中
Pop(Optr,e); //出栈元素e
}
else
break; //e是非'^'时退出循环
}
Push(Optr,*exp); //将'^'进栈
exp++; //继续扫描其他字符
break;
case 'l': //判断为log号
while(!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr,e); //取栈顶元素e
if(e=='l') //e是'l'
{
postexp[i++]=e; //将e存放到postexp中
Pop(Optr,e); //出栈元素e
}
else
break; //e是非'l'时退出循环
}
Push(Optr,*exp); //将'l'进栈
exp+=3; //继续扫描其他字符
break;
case'c': //判断为cos号
while(!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr,e); //取栈顶元素e
if(e=='c') //e是'l'
{
postexp[i++]=e; //将e存放到postexp中
Pop(Optr,e); //出栈元素e
}
else
break; //e是非'c'时退出循环
}
Push(Optr,*exp); //将'c'进栈
exp+=3; //继续扫描其他字符
break;
case's': //判断为sin号
while(!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr,e); //取栈顶元素e
if(e=='s') //e是's'
{
postexp[i++]=e; //将e存放到postexp中
Pop(Optr,e); //出栈元素e
}
else
break; //e是非's'时退出循环
}
Push(Optr,*exp); //将's'进栈
exp+=3; //继续扫描其他字符
break;
case't': //判断为tan号
while(!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr,e); //取栈顶元素e
if(e=='t') //e是't'
{
postexp[i++]=e; //将e存放到postexp中
Pop(Optr,e); //出栈元素e
}
else
break; //e是非't'时退出循环
}
Push(Optr,*exp); //将't'进栈
exp+=3; //继续扫描其他字符
break;
default: //处理数字字符
while( *exp>='0'&&*exp<='9'||*exp=='.') //判定为数字和小数点字符
{
postexp[i++]= * exp;
exp++;
}
postexp[i++]='#'; //用#标识一个数字串结束
}
}
while(!StackEmpty(Optr)) //此时exp扫描完毕,栈不空时循环
{
Pop(Optr,e); //出栈元素e
postexp[i++]=e; //将e放到postexp中
}
postexp[i]='\0'; //给postexp表达式添加结束的标志
DestroyStack(Optr); //销毁栈
}
double compvalue(char *postexp) //计算后缀表达式的值
{
double d,a,b,c,e;
int n,m=1,w=1;
SqStack1*Opnd; //定义操作数栈
InitStack(Opnd); //初始化操作数栈
while( *postexp!='\0') //postexp字符串未扫描完时循环
{
switch( *postexp)
{
case'+': //判定为'+'号
Pop1(Opnd,a); //出栈元素a
Pop1(Opnd,b); //出栈元素b
c=b+a; //计算c
Push1(Opnd,c); //将计算结果c进栈
break;
case'-': //判定为'-'号
Pop1(Opnd,a); //出栈元素a
Pop1(Opnd,b); //出栈元素b
c=b-a; //计算c
Push1(Opnd,c); //将计算结果c进栈
break;
case'*': //判定为'*'号
Pop1(Opnd,a); //出栈元素a
Pop1(Opnd,b); //出栈元素b
c=b*a; //计算c
Push1(Opnd,c); //将计算结果c进栈
break;
case'/': //判定为'/'号
Pop1(Opnd,a); //出栈元素a
Pop1(Opnd,b); //出栈元素b
if(a!=0)
{
c=b/a; //计算c
Push1(Opnd,c); //将计算结果c进栈
break;
}
else
{
printf("\n\t除零错误!\n");
exit(0); //异常退出
}
case'^': //判定为'^'号
Pop1(Opnd,a); //出栈元素a
Pop1(Opnd,b); //出栈元素b
c=pow(b,a); //计算c
Push1(Opnd,c); //将计算结果c进栈
break;
case'l': //判定为log函数
Pop1(Opnd,a); //出栈元素a
Pop1(Opnd,b); //出栈元素b
c=(log(a))/(log(b)); //计算c
Push1(Opnd,c); //将计算结果c进栈
break;
case'c': //判定为cos函数
Pop1(Opnd,a); //出栈元素a
c=cos(a); //计算c
Push1(Opnd,c); //将计算结果c进栈
break;
case's': //判定为sin函数
Pop1(Opnd,a); //出栈元素a
c=sin(a); //计算c
Push1(Opnd,c); //将计算结果c进栈
break;
case't': //判定为tan函数
Pop1(Opnd,a); //出栈元素a
c=tan(a); //计算c
Push1(Opnd,c); //将计算结果c进栈
break;
default:
d=0; //处理数字字符
n=0;
while( *postexp>='0'&& *postexp<='9'||*postexp=='.'||*postexp=='@') //判定为数字或者小数点
{
if(*postexp=='@')
{m=-1;
*postexp++;}
d=10*d+ *postexp-'0';
postexp++;
if( *postexp=='.') //处理小数点之后的数字
{
W=-1;
*postexp++; //跳过小数点
n=0; //无论前面n加了多少次,一旦遇到'.'就从小数点之后开始重新计数
}
n++;
}
d=d/pow(10,(n-1));
d=m*d;
if(w!=-1)
{
d=d*pow(10,n-1);
}
m=1;
w=1;
Push1(Opnd,d); //将数值d进栈
break;
}
postexp++; //处理其他字符
}
GetTop1(Opnd,e); //取栈顶元素
DestroyStack1(Opnd); //销毁栈
return e; //返回e
}
void behind(char*postexp) //输出后缀表达式
{
int t;
t=strlen(postexp);
for(int i=0;i<t;i++)
{
if(*(postexp+i)=='s') {cout<<"sin";continue;}
if(*(postexp+i)=='l') {cout<<"log";continue;}
if(*(postexp+i)=='c') {cout<<"cos";continue;}
if(*(postexp+i)=='t') {cout<<"tan";continue;}
if(*(postexp+i)=='@') {cout<<"-";continue;}
cout<<*(postexp+i);
}
cout<<endl;
}
int main()
{
while(1)
{
int q;
cout<<"**- 请输入算术表达式 -**"<<endl;
char exp[MaxSize];
char postexp[MaxSize];
cin>>exp; //输入算术表达式
trans(exp,postexp);
cout<<"中缀表达式:"<<exp<<endl;
cout<<"后缀表达式";
behind(postexp);
cout<<"表达式的值"<<compvalue(postexp)<<endl;
cout<<"enter '1' clear the screen "<<endl;
cin>>q;
switch(q)
{
case 1:system("cls");
default :break;
}
}
return 1;
}