课程设计中的题目,要求设计一个程序将输入中缀表达式转换成后缀表达式,再将根据后缀表达式计算出对应的数值,这也应该就是所谓的计算器在处理用户所输入的算式时,所作出的计算过程。
如:输入中缀表达式:(1+2)*3+4 ,计算器会将其转换为后缀表达式:1 2 + 3 * 4 +,其中去除了括号,然后计算器再根据特定的算法将其计算成数值:13。
这个程序就是类似的工作过程。其中使用到了链栈的数据机构,和基本的C++语法。
1.总体分析。
由于时间比较匆忙,所以程序的大多数算法和方法的设计都没有进行优化,但是主要功能齐全无误。而且不仅仅可以计算常规的四则运算,还可以混合逻辑运算的 “|”、“&”以及关系运算的 “>”、“<”、“=”一起进行运算。其中对于表达式的检测设计的不够详细,只是对其简单的进行判断,且方法较为冗余,仅供参考。
出了菜单之外,一共设计了两个类:
Stack:用来处理栈的各类操作。
Expression:用来实现表达式的具体转换过程和逻辑。
其中Expression类还可以再分为三个部分:中缀表达式转后缀表达式、后缀表达式转中缀表达式、后缀表达式计算数值。同样的,后来认为也可以将这三部分分开使用三个类的实现比较清晰。
2.部分代码构思。
首先是Stack类的设计。
class Stack
{
private:
typedef struct StackNode//栈的节点
{
char data;//用来存放运算符数据
int number;//用来存放数字
struct StackNode *next;
} StackNode;
typedef struct StackLink//栈的头结点,用来存放栈的长度
{
StackNode *top;//指向栈顶
int count;//统计栈的元素个数
} StackLink;
public:
StackLink SL;
Stack()//初始化操作
{
CreateLink(SL);//创建一个空的链栈
}
void CreateLink(StackLink &SL)//初始化链栈
{
SL.count = 0;
SL.top = NULL;
}
void pushElem(char ch) //运算符入栈
{
StackNode* Q;//新的节点
Q = (StackNode*)malloc(sizeof(StackNode));
Q->data = ch;
Q->next = SL.top;//让新的节点指向栈顶
SL.top = Q;//指向栈顶
SL.count++;
}
void pushElem(int number)//用于入栈数字
{
StackNode* Q;//新的节点
Q = (StackNode*)malloc(sizeof(StackNode));
Q->number = number;
Q->next = SL.top;//让新的节点指向栈顶
SL.top = Q;//指向栈顶
SL.count++;
}
int popElemNumber()//出栈栈顶数值并返回
{
int num;
StackNode* Q;
Q = SL.top;//记录栈顶元素
num = Q->number;
SL.top = SL.top->next;//指向栈顶的下一个元素
free(Q);//释放节点s
SL.count--;
return num;
}
void popElem() //出栈
{
StackNode* Q;
Q = SL.top;//记录栈顶元素
SL.top = SL.top->next;//指向栈顶的下一个元素
free(Q);//释放节点s
SL.count--;
}
void destroyStack()//清空栈
{
while(SL.count != 0)
{
StackNode* Q;
Q = SL.top;//记录栈顶元素
SL.top = SL.top->next;//指向栈顶的下一个元素
free(Q);//释放节点s
SL.count--;
}
}
};
代码后面的注释较为齐全,总体来说就是实现了出栈操作和入栈操作、初始化栈和清空栈的操作。其中需要注意的是在这里重载了入栈的的方法,使其可以入栈char类型和int类型,这是因为后面需要两次用到这个栈来进行操作,并且入栈的类型不同,所以重载一下这个方法比较方便计算。
其次是Expression类。
先看一下类的属性:
private:
string Z1;//用来存入中缀表达式
char H2[100];//用来存入输入的后缀表达式
char H3[100];//用来后缀计算数值
char Z2[100];//用来保存后缀转中缀后的表达式
char H1[100];//用来保存中缀转后缀表达式
int zLength;//用来计算中缀表达式的长度
int hLength;//用来计算后缀表达式的
int houLength;//用来求数值的时候用的
int number;//用来保存数值
int zCount;//用来对中缀表达式进行处理
int hCount;//用来对后缀表达式进行处理
int houCount;//用来对后缀表达式进行部分处理
Stack S1;//链栈,用来处理中缀转后缀
Stack S2;//处理后缀计算数值
以后所出现的属性都在这里了,命名方面有些欠缺。
1)中缀表达式转后缀表达式。
首先需要输入中缀表达式,然后计算所输入内容的长度,用来后面的计算使用。
void setZ1()//输入中缀表达式
{
cout << "\n\n\n请输入中缀表达式:";
cin >> Z1;
ZLength();
}
void ZLength()//计算中缀表达式长度
{
zLength = 0;
int i = 0;
while(Z1[i] != '\0')
{
zLength++;
i++;
}
}
其次是其中的核心代码:
int zToH() //中缀转化为后缀
{
if(!checkBracket())
{
cout << "\n\n\n输入错误,括号不匹配!";
return 0;
}
if(!checkOthers())
{
cout << "\n\n\n输入错误!";
return 0;
}
zCount = 0;
hCount = 0;
char topCh;
char nowCh;
while(zCount < zLength)
{
//判断这个是数字还是运算符
if(judgeNumOrAri(Z1[zCount])) //如果是数字
{
H1[hCount] = Z1[zCount];//将数字存入到后缀字符中
hCount++;//后缀下标+1
nextChar(zCount,hCount);//判断这个数字的下一位是不是数字,直到遇到符号结束
H1[hCount] = ' ';//用空格分隔开数字
hCount++;
}
else //如果是运算符
{
if(S1.SL.count == 0)//此时为空栈
{
S1.pushElem(Z1[zCount]);//将运算符入栈
}
else//不为空栈的话需要判断此时的运算符与栈顶元素的运算符优先级,以及出栈的操作
{
topCh = S1.SL.top->data;//取得此时栈顶元素的运算符
nowCh = Z1[zCount];//取得此时中缀表达式中的运算符
if(nowCh=='(')//如果是左括号的话,直接压栈
{
S1.pushElem(Z1[zCount]);
}
else//如果是其他符号的话,需要进行比较了
{
if(nowCh==')')//如果是右括号
{
while(topCh!='(' && S1.SL.count!=0)//如果栈顶符号不是左括号或者空,一直出栈
{
H1[hCount] = topCh;//将栈顶元素 赋值给后缀表达式
hCount++;
H1[hCount] = ' ';//用空格分隔开数字
hCount++;
S1.popElem();//栈顶元素出栈
if(S1.SL.top != NULL)
{
topCh = S1.SL.top->data;
}
}
if