问题描述
这里限定的表达式求值问题是: 用户输入一个包含“+”、“-”、“*”、“/”、正整数和圆括号的合法数学表达式,计算该表达式的运算结果。本问题采用顺序栈存储结构。
算术表达式求值过程是: STEP 1:先将算术表达式转换成后缀表达式。STEP 2:然后对该后缀表达式求值。
(1)中缀表达式exp ==》后缀表达式postexp伪代码如下
初始化运算符栈op;
将'='进栈;
从exp读取字符ch;
while (ch!='\0')
{ if (ch不为运算符)
将后续的所有数字均依次存放到postexp中,并以字符'#'标志数值串结束;
else
switch(Precede(op栈顶运算符,ch))
{
case '<': //栈顶运算符优先级低
将ch进栈; 从exp读取下字符ch; break;
case '=': //只有栈顶运算符为'(',ch为')'的情况
退栈; 从exp读取下字符ch; break;
case '>': //栈顶运算符应先执行,所以出栈并存放到postexp中
退栈运算符并将其存放到postexp中; break;
}
}
若字符串exp扫描完毕,则将运算符栈op中'='之前的所有运算符依次出栈并存放到postexp中。最后得到后缀表达式postexp;
(2)对后缀表达式postexp求值伪代码如下
while (从postexp读取字符ch,ch!='\0')
{ 若ch为数字,将后续的所有数字构成一个整数存放到数值栈st中。
若ch为“+”,则从数值栈st中退栈两个运算数,相加后进栈st中。
若ch为“-”,则从数值栈st中退栈两个运算数,相减后进栈st中。
若ch为“*”,则从数值栈st中退栈两个运算数,相乘后进栈st中。
若ch为“/”,则从数值栈st中退栈两个运算数,相除后进栈st中(若除数为零,则提示相应的错误信息)。}
若字符串postexp扫描完毕,则数值栈op中的栈顶元素就是表达式的值。
解题思路
将算术表达式转换为后缀表达式的主要思路为:在从左到右扫描算术表达式exp的一个运算符op时,如果栈为空或者栈顶为’(‘,直接将其进栈;如果栈不空,只有当op的优先级高于栈顶运算符的优先级时才直接将op进栈;否则依次出栈运算符并存人后缀表达式postexp。直到栈顶运算符的优先级小于op的优先级为止,然后再将op进栈。另外,在扫描exp遇到一个运算符op时,如果op为’(‘,表示一个子表达式的开始,直接将其进栈;如果op为’)’,表示一个子表达式的结束,需要出栈运算符并存人postexp,直到栈顶为’(‘,再将’(‘出栈。
后缀表达式求值主要思路:从左到右扫描后缀表达式postexp,若读取的是一个操作数,将它进操作数栈。若读取的是一个运算符op,则从操作数栈中连续出栈两个操作数a和b,计算b op a的值,并将计算结果进操作数栈。当整个后级表达式扫描结束时,操作数栈中的栈顶元素就是表达式的计算结果。
代码实现
#include<iostream>
using namespace std;
#define MAXSIZE 50
//运算符栈类型定义
typedef struct
{
char data[MAXSIZE];
int top;
}StackOptr;
//操作数栈类型定义
typedef struct
{
double data[MAXSIZE];
int top;
}StackOpnd;
//初始化运算符栈
void InitStackOptr(StackOptr*& s)
{
s = new StackOptr;
s->top = -1;
}
//初始化操作数栈
void InitStackOpnd(StackOpnd*& s)
{
s = new StackOpnd;
s->top = -1;
}
//判断栈是否为空
template<typename T1>
bool StackEmpty(T1& s)
{
if (s->top == -1)
return true;
return false;
}
//销毁栈
template<typename T1>
void DestroyStack(T1& s)
{
delete s;
}
//进栈
template<typename T1, typename T2 >
bool Push(T1& s, T2 e)
{
if (s->top == MAXSIZE - 1)
return false;
s->top++;
s->data[s->top] = e;
return true;
}
//出栈
template<typename T1, typename T2 >
bool Pop(T1& s, T2& e)
{
if (s->top == -1)
return false;
e = s->data[s->top];
s->top--;
return true;
}
//取栈顶元素
template<typename T1, typename T2 >
bool GetTop(T1& s, T2& e)
{
if (s->top == -1)
return false;
e = s->data[s->top];
return true;
}
// 将中缀表达式exp转换成后缀表达式postexp
void trans(char* exp, char postexp[])
{
char e; // 给Pop与GetTop函数使用
StackOptr* Optr; //定义运算符栈指针
int i = 0; // i作为postexp 下标
InitStackOptr(Optr); //初始化运算符栈
while (*exp != '\0') //exp表达式未扫描时循环
{
switch (*exp)
{
case '(': //判定为左括号
Push(Optr, '(');
exp++; //exp指针前移,继续处理下一个字符
break;
case ')': //判定为右括号
Pop(Optr, e); //一直出栈,直到遇到')'为止
while (e != '(')
{
postexp[i++] = e;
Pop(Optr, e);
}
exp++;
break;
case '+': // 判定为'+'或'-'号
case '-':
while (!StackEmpty(Optr)) //直到栈空或者栈顶为'(',然后将其入栈
{
GetTop(Optr, e);
if (e == '(')
break;
else
{
postexp[i++] = e;
Pop(Optr, e);
}
}
Push(Optr, *exp); //最后将 '+' 或'- '入栈
exp++;
break;
case '*': //判定为'*'或'/'号
case '/':
while (!StackEmpty(Optr)) //直到栈空或者栈顶为'(','*' 或'/ ',然后将其入栈
{
GetTop(Optr, e);
if (e == '/' || e == '*')
{
postexp[i++] = e;
Pop(Optr, e);
}
else
break;
}
Push(Optr, *exp); //最后将 '*' 或'/ '入栈
exp++;
break;
default: //处理数字字符
while (*exp >= '0' && *exp <= '9') //循环判断处理多位数字
{
postexp[i++] = *exp;
exp++;
}
postexp[i++] = '#'; //以#标识一个数字串结束
}
}
while (!StackEmpty(Optr)) //此时exp扫描完毕,栈不空时循环
{ //将Optr中的的所有运算符依次出栈并存放到postexp
Pop(Optr, e);
postexp[i++] = e;
}
postexp[i] = '\0'; //给postexp表达式添加结束标志
DestroyStack(Optr); //销毁栈
}
//计算后缀表达式的值
double compvalue(char* postexp)
{
StackOpnd* Opnd; //定义操作数栈
InitStackOpnd(Opnd); //初始化操作数栈
double result; //结果
double a, b; //弹出栈的两个数
double c; //计算弹出栈的两个数的算术运算结果
double d; //将连续的数字字符转换成十进制整数保存在d里
while (*postexp != '\0') //postexp字符串未扫描完时循环
{
switch (*postexp)
{
case '+': //判定为'+'号
Pop(Opnd, a); //从Opnd栈中出栈两个数值a和b,c=a+b,将c入栈
Pop(Opnd, b);
c = b + a;
Push(Opnd, c);
break;
case '-': //判定为'-'号
Pop(Opnd, a); //从Opnd栈中出栈两个数值a和b,c=b-a,将c入栈
Pop(Opnd, b);
c = b - a; //注意是b-a,因为b后出栈
Push(Opnd, c);
break;
case '*': //判定为'*'号
Pop(Opnd, a); //从Opnd栈中出栈两个数值a和b,c=b*a,将c入栈
Pop(Opnd, b);
c = b * a;
Push(Opnd, c);
break;
case '/': //判定为'/'号
Pop(Opnd, a); //从Opnd栈中出栈两个数值a和b,若a!=0,则c=b/a,将c入栈
Pop(Opnd, b);
if (a != 0) //检查除数的合法性,若不合法,则退出程序
{
c = b / a;
Push(Opnd, c);
break;
}
else
{
cout << "\n除零错误!" << endl;
exit(0);
}
default: //处理数字字符
d = 0; //将连续的数字字符转换为对应数值存放到d中
while (*postexp >= '0' && *postexp <= '9')
{
d = 10 * d + *postexp - '0';
postexp++;
}
Push(Opnd, d);
}
postexp++; //继续处理下一个字符
}
GetTop(Opnd, result); //取栈顶元素,即运算最终结果
delete Opnd; //销毁栈
return result; //返回结果
}
int main()
{
char exp[MAXSIZE]; //前缀表达式
char postexp[MAXSIZE]; //后缀表达式
cout << "请输入合法的中缀表达式:";
cin >> exp;
trans(exp, postexp);
cout << "后缀表达式:" << postexp << endl //输出后缀表达式和表达式结果
<< "表达式结果:" << compvalue(postexp) << endl;
}
运行结果
如有同校学弟学妹们见到这篇博客,切忌不要随意CV,请自主多动手多思考。