一、问题描述
用户输入一个包含“+”、“-”、“*”、“/”、正整数和圆括号的合法数学表达式,计算该表达式的运算结果。
二、实现思路
在基于顺序栈实现表达式求值中,该算法主要分为将算术表达式转换成后缀表达式和后缀表达式求值两个步骤。
将算术表达式转换为后缀表达式的主要思路为:在从左到右扫描算术表达式exp的一个运算符op时,如果栈为空或者栈顶为’(‘,直接将其进栈;如果栈不空,只有当op的优先级高于栈顶运算符的优先级时才直接将op进栈;否则依次出栈运算符并存人后缀表达式postexp。直到栈顶运算符的优先级小于op的优先级为止,然后再将op进栈。另外,在扫描exp遇到一个运算符op时,如果op为’(‘,表示一个子表达式的开始,直接将其进栈;如果op为’)’,表示一个子表达式的结束,需要出栈运算符并存人postexp,直到栈顶为’(‘,再将’(‘出栈。
中缀表达式exp ==》后缀表达式postexp伪代码如下:
后缀表达式求值主要思路:从左到右扫描后缀表达式postexp,若读取的是一个操作数,将它进操作数栈。若读取的是一个运算符op,则从操作数栈中连续出栈两个操作数a和b,计算b op a的值,并将计算结果进操作数栈。当整个后级表达式扫描结束时,操作数栈中的栈顶元素就是表达式的计算结果。
对后缀表达式postexp求值伪代码如下:
三、解题代码
#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;
}