一、题目
利用堆栈的基本操作来实现中缀表达式的计算。该中缀表达式中包括:+、-、*、/、(、)和整数。
二、思路
1、主函数的整体思路:首先检查表达式是否合法,然后开始读取表达式的字符,分别定义一个操作符栈,一个操作数栈。操作符栈在栈底存一个‘#’,且在表达式后加一个‘#’。遇到操作数就存入操作数栈,遇到操作符就要分情况讨论:①读取的比栈顶的优先级高,存入栈中。②读取的比栈顶优先级低,将栈顶元素弹出,再弹出操作数栈的两个数进行运算(后弹出的数在运算符的左边的运算),将结果存入操作数栈,再将读取的字符存入栈中。③读取的是‘(’,则直接存入栈中(体现在运算符的优先级中)。④读取的是‘)’,将操作符栈中的元素依次弹出计算,直到遇到‘(’。⑤读取的是‘#’,就直接全部计算,直到弹出的操作符栈的元素为‘#’。最后留在操作栈的数字就是最终结果。
2、关于检查表达式是否合法(需要算上空格的情况):
比如表达式3 * 9 -(14+7),考虑每一个字符前的字符的合法情况,一个数字的前一个除去空格外的字符只能是操作符或数字或左括号,不能是一个数字;一个操作符的前一个除去空格的字符只能是数字;左括号的前一个除空格外的字符只能是操作符;右括号的前一个除空格外的字符只能是数字;并且,有了左括号一定就有右括号。有了这些限制条件,就可以用一些条件语句来完成一个检查表达式的函数check():
2、关于存储运算符和操作数的栈的类型,我设定为了int型,因为字符是可以用ascii码来表示的,与整型之间相互转换比较简单。或者也可以定义两种类型的栈,一个整型的用来存放操作数,一个字符型的用来存放操作符,不过我感觉和直接定义成整型区别不大。
3、关于表达式中操作数是多位数,可以用一个循环把数字读取到一个数组里,再从数组里分别读出并乘上对应的位数的10次幂。
4、关于测定运算符优先级的函数,以及运算的函数。运算符的优先级是,乘除和右括号优先级相等且最大,加减优先级其次,左括号再次,井号最小。
三、具体的代码实现
栈的定义与基本操作,创建一个LinkStack.h头文件放在里面。
#pragma once
#include<iostream>
using namespace std;
typedef int Elemtype;
typedef struct SNode
{
Elemtype data;
SNode* next;
}SNode,*LinkStack;
void InitStack(LinkStack& S)
{
S = NULL;
}
void Push(LinkStack& S, Elemtype e)
{
SNode* p;
p = new SNode;
p->data = e;
p->next = S;
S = p;
}
bool Pop(LinkStack& S, Elemtype& e)
{
if (S)
{
SNode* p;
e = S->data;
p = S;
S = S->next;
delete p;
return true;
}
return false;
}
bool StackEmpty(LinkStack L)
{
if (L == NULL)
return true;
else return false;
}
void Show(LinkStack L)
{
SNode* p = L;
while (p)
{
cout << p->data<<" ";
p = p->next;
}
cout << endl;
}
源文件
#include<iostream>
#include"LinkStack.h"
using namespace std;
int check(char* p);//检查表达式是否规范
char Operate_S(char c1, char c2, char op);//对c1和c2进行运算符为op的运算,返回结果
int opcompare(char c1, char c2); //比较运算符的优先级,如果c1优先于c2,则返回1,否则返回0
int main()
{
char exp[81] = "3 + 6 * 8 - (42 + 8 / 4 )";
if (!check(exp))
{
cout << "表达式有误!";
return 0;
}
char* p = exp;
*(p + strlen(p)) = '#';//在原有的表达式后加上#,用于判断表达式读取结束
int num = 0;//操作数
LinkStack S1,S2;//S1是操作数栈,S2是操作符栈
InitStack(S1);
InitStack(S2); Push(S2, '#');
while (*p)
{
while(*p == ' ')//跳过空格
p++;
if ( *p>47 && *p<58 )//p为数字
{
int n[10]= {0};//将多位数字暂时存入数组
int i;
for (i = 0; *p > 47 && *p < 58; p++, i++)//将多位数字暂时存入数组
{
n[i] = *p-48;
}
for (int j = 0; j<i; j++)//得到操作数
{
num += n[j] * pow(10, i-j-1);
}
Push(S1, num);
num = 0;//重置num
}
else
{
int e ,m, n;
if (*p == '(')
Push(S2, *p);
else if (*p == ')')//遇到右括号需要一直计算到左括号
{
Pop(S2, e);
while (e != '(')
{
Pop(S1, m);
Pop(S1, n);
Push(S1,Operate_S(n, m, e));
Pop(S2, e);
}
}
else if (*p == '#')//说明表达式读取完毕,将栈中的运算符依次计算,直到栈底
{
Pop(S2, e);
while (e != '#')
{
Pop(S1, m);
Pop(S1, n);
Push(S1, Operate_S(n, m, e));
Pop(S2, e);
}
}
else
{
if (opcompare(S2->data, *p))//与栈顶的运算符比较优先级,如果*p优先级更高,就存入栈中,否则将栈顶元素弹出进行运算。
{
Pop(S2, e);
Pop(S1, m); Pop(S1, n);
Push(S1, Operate_S(n, m, e));
Push(S2, *p);
}
else
Push(S2, *p);
}
p++;
}
}
Pop(S1, num);
cout << "The expression:" << exp<<endl;
cout << "The result:" << num;
return 0;
}
int check(char* p)
{
char e;
int spacetag = 0; //若是空格就设为1
int pretag = 0; //若是运算符设为0,数字设为1,左括号设为2,右括号设为3
LinkStack S; //用于存储括号
InitStack(S);
while (*p)
{
e = *p;
if (*p > 47 && *p < 58)//e为操作数
{
if ((spacetag == 1 && pretag == 1) || pretag == 3)
return 0;
pretag = 1;
spacetag = 0;
}
else if (e == ' ')
{
spacetag = 1;
}
else if (*p > 41 && *p < 44 || e == 45 || e == 47)//e为运算符
{
if (pretag == 0 || pretag == 2)
return 0;
pretag = 0;
spacetag = 1;
}
else if (e == '(')
{
if (pretag != 0)
return 0;
Push(S, e);
pretag = 2;
spacetag = 1;
}
else if (e == ')')
{
if (pretag != 1)
return 0;
int c; Pop(S, c);
if (c != '(')
return 0;
pretag = 3;
spacetag = 1;
}
else return 0;
p++;
}
if (!StackEmpty(S))
return 0;
else return 1;
}
char Operate_S(char c1, char c2, char op)
{
switch (op)
{
case '+':return c1 + c2;
case '-':return c1 - c2;
case '*':return c1 * c2;
case '/':return c1 / c2;
}
}
int opcompare(char c1, char c2)
{
if (c1 == '#' || ((c1 == '+' || c1 == '-') && (c2 == '*' || c2 == '/'))||c1=='(')
return 0;
else return 1;
}
运行结果: