线性表的实现与应用
算数表达式中缀转后缀并求值
一、 实验目的
1. 熟练掌握线性表在顺序存储结构和链式存储结构中的特点以及相关的查找、插入、删除等基本操作的实现方法,并且能够从时间和空间复杂度的角度综合考虑两种不同存储结构的不同特点。
2. 栈是计算机科学中应用非常广泛的数据结构之一,通过将中缀表达式转为后缀表达式来加深对栈的理解,并且学会用栈来解决实际中的简单问题。
3. 训练逻辑性的思维,通过对算数表达式求值问题的深入思考,加深对数据结构的认识。
4. 培养程序设计,算法分析以及调试程序的能力,为日后工作中参与项目打下基础。
二、 实验要求及实验环境
1. 实验要求
(1) 将用户输入的任一合法的中缀表达式转为后缀表达式;
(2) 利用变换后的表达式求解;
(3) 输出时应该既有后缀表达式又有表达式的值。
2.实验环境
(1)操作系统:Microsoft Windows 8.1 Enterprise x64.
(2)编译环境:Microsoft Visual Studio 2013 Ultimate
三、设计思想
(1)栈模板类的实现
在实现运算符运算符栈和操作数栈时,用到了自己构造的C++模板类。
具体数据类型及函数操作如下:
template<classType> struct Stack
{
int length; //数据长度
Type data[MAXLENGTH]; //存储数据
Stack( ) //初始化栈(置空)。
voidPush(const Type &element) //将元素压入栈中
Type Pop( ) //将栈顶元素弹出并从栈中删除
Type &Top( ) //返回栈顶元素<pre name="code" class="cpp"><pre name="code" class="cpp"> void clear( ) //清空栈
bool empty( ) //判断栈是否为空。
};
Stack<char> Operators; //构建对象,存储运算符的栈。
Stack<double> Numbers; //构建对象,存储操作数的栈。
(2)表达式括号是否合法、匹配
函数原型:
<bool DealIllegalInfix(char *InfixExpre); //判断输入表达式的括号是否合法、匹配。
//InfixExpre为输入的中缀表达式,函数返回值为bool类型。
(3)预处理中缀表达式
对于表达式开头直接为负数或者表达式中左括号后紧跟负数的情况进行预处理:
① 如果是在开始位置,表达式前直接加‘0‘;
② 如果是在左括号后的负数,在该负数前加‘0‘;
// 函数原型:<pre name="code" class="cpp"> voidExpressionDeal(char *InfixExpre);// 该函数无返回值,参数InfixExper为要处理的中缀表达式的头指针。
//函数实现思想:对中缀表达式InfixExper的每个元素进行遍历,遇到在开始位置是负数或者左括号后是负数的情况,进行补‘0‘。
(3)运算符优先级处理
运用函数处理两个运算符优先级的比较问题,①遇到左括号直接压栈;②*、/、%、的优先级高于+、-、(③+、-的优先级高于(
//函数实现:
intCompareOperator(char x, char y) //函数返回值为int,需要两个char类型参数x,y,如果y的优先级高于x,返回1,否则返回0.
(4)处理后的中缀表达式转为后缀表达式
基本算法:
遍历中缀表达式:1.如果是操作数,读取其所有的位,然后进入后缀表达式;
2.如果是操作符( + – * / % )2.1 如果“操作符栈”为空,直接入栈;
2.2 如果当前操作符优先级高于栈顶元素优先级,那么入栈;
2.3 如果当前操作符优先级低于栈顶元素优先级,那么栈顶操作符出栈,循环执行;
3.如果是左括号'(',直接入栈4.如果是右括号')',如果栈非空,那么栈顶元素出栈,直到遇到左括号'(';
5.遍历结束中,将操作符栈中的元素依次出栈,添加到后缀表达式。
//函数原型:
voidTransformExpression(char *InfixExpre, char *SuffixExpre) <pre name="code" class="cpp">//该函数无返回值,需要两个char类型的参数,InfixExpre为输入的中缀表达式,SuffixExpre为后缀表达式。
(5)后缀表达式的预处理
由于运算数在实数范围,所以需要从字符串中将实数分离出来,然后利用C语言STL模板库函数atof()将字符串转化为实数
(★注:在随后的程序优化中,可以考虑自己实现 atof() 函数的功能。)
//函数原型:
voidStringToFloat(char *SuffixExpre) //函数无返回值,需要输入一个char类型的指针参数,SuffixExpre为需要处理的后缀表达式。
(6)计算后缀表达式
算法分析:
从后缀表达式队列中依次取出元素
1.如果是操作数,那么将其压入“结果栈”中;
2.如果是操作符,从“结果栈”中取出两个元素,进行计算。(注意从栈中取元素的顺序和操作数的顺序是相反的)
遍历后缀表达式结束后,“结果栈”中的元素就是最终的结果。
//函数原型:
voidDealSuffix(char *SuffixExpre, float *OperatorNumbers)
//函数无返回值,需要传入两个参数,一个为char类型的字符串,表示处理后的后缀表达式,另一个是float类型的数组,用来存储分离出来的操作数。
源代码
/**
Created on 10 14 17:11:01
Tested under Windows 8.1 Enterprise 64bits
Version 1.3
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLENGTH 100
/**构造栈模板类*/
template<class Type> struct Stack
{
int length; //数据长度
Type data[MAXLENGTH]; //存储数据
Stack():length(0){} //初始化栈(置空)。
//将元素压入栈中
void Push(const Type &element)
{
data[length++] = element;
}
//将栈顶元素弹出并从栈中删除
Type Pop()
{
return data[--length];
}
//返回栈顶元素
Type &Top()
{
return data[length-1];
}
//清空栈
void clear()
{
length = 0;
}
//判断栈是否为空。
bool empty()const
{
return !length;
}
};
/**函数申明*/
bool DealIllegalInfix(Stack <char> Parenthesis, char *InfixExpre); //判断输入表达式的括号是否合法、匹配。
void ExpressionDeal(char *InfixExpre); //中缀表达式预处理。
bool CompareOperator(char x, char y); //比较两个运算符的优先级。
void TransformExpression(Stack<char> Operators, char *InfixExpre, char *SuffixExper); //中缀表达式转后缀表达式。
void StringToFloat(Stack<float> Numbers, char *SuffixExpre); //将小数、多位数从字符串中剥离。
void DealSuffix(Stack<float> Numbers, char *SuffixExpre, float *OperatorNumbers); //处理后缀表达式,进行计算。
float CalculateResult(char operators, float number1, float number2); //对操作数进行计算。
/**主函数*/
int main()
{
int i, flag = 1;
char InfixExpre[MAXLENGTH]; //中缀表达式。
char SuffixExpre[MAXLENGTH]; //后缀表达式。
bool key = 0; //标识符。
printf("\n\t算术表达式计算系统\n------------------------------------\n");
printf("@copyright 1130310427 Zhiming Liu HIT CS 2013 \nVersion:1.3\n");
printf("\n提示:此程序用于实现算术表达式求值,可以处理实数\n范围(包括多位数,负数,小数)的加、减、乘、除、求余运算,\n可以判别括号是否匹配,请输入合法的中缀表达式!\n");
while(flag == 1) //用于多次输入计算的循环
{
Stack <char> Operators; //构建对象,存储运算符的栈。
Stack <float> Numbers; //构建对象,存储操作数的栈。
Stack <char> Parenthesis; //存储括号的栈
printf("\n请输入计算表达式:");
scanf("%s", InfixExpre);
while(key == 0)
{
key = DealIllegalInfix(Parenthesis, InfixExpre); //判断中缀表达式的左右括号是否匹配
if(key == 0) //当输入的表达式左右括号不匹配时
{
printf("\n请再次输入表达式:");
scanf("%s", InfixExpre);
}
}
ExpressionDeal(InfixExpre); //对中缀表达式进行预处理
TransformExpression(Operators, InfixExpre, SuffixExpre);//将预处理后的中缀表达式转为后缀表达式
printf("\n后缀表达式:%s\n", SuffixExpre);
StringToFloat(Numbers, SuffixExpre);//计算后缀表达式的结果并输出。
printf("\n继续计算请键入1,退出请键入0:");
scanf("%d", &flag);
if(flag == 1)
{
printf("-----------------------------\n\n");
}
else if(flag == 0)
{
printf("\n~~程序已退出,谢谢您的使用!~~\n");
}
}
return 0;
}
/**处理括号输入不合法中缀表达式*/
bool DealIllegalInfix(Stack <char> Parenthesis, char *InfixExpre)
{
int i = 0;
while(InfixExpre[i] != '\0')
{
if(InfixExpre[i] == '(') //如果是左括号,直接压栈。
{
Parenthesis.Push(InfixExpre[i]);
}
else if(InfixExpre[i] == ')') //遇到又括号时
{
if(Parenthesis.empty()) //如果栈为空
{
printf("\n##有右括号没有匹配!##\n");
return false;
}
Parenthesis.Pop();
}
i++;
}
if(!Parenthesis.empty()) //遍历结束,如果栈非空
{
printf("\n##有左括号没有匹配!##\n");
return false;
}
return true;
}
/**负数处理*/
/**对中缀表达式 开头'-' 和 '(' 后的 '-' 的处理*/
void ExpressionDeal(char *InfixExpre)
{
int i, j, k, t;
char Exptemp[MAXLENGTH];
/*处理表达式开头为负数的情况*/
if(InfixExpre[0] == '-')
{
strcpy(Exptemp, InfixExpre);//将中缀表达式存储到临时字符数组exptemp.
InfixExpre[0] = '0';//表达式头补 '0'
for(i = 0; Exptemp[i] != '\0'; i++)
{
InfixExpre[i+1] = Exptemp[i];
}
}
/*处理表达式中负数左侧为左括号的情况*/
for(j = 0; j < (strlen(Exptemp)-1); j++)
{
strcpy(Exptemp, InfixExpre);//将中缀表达式存储到临时字符数组exptemp.
if(Exptemp[j] == '(' && Exptemp[j+1] == '-')
{
for(k = 0; k < (j+1); k++)
{
InfixExpre[k] = Exptemp[k]; // 把表达式的符合条件的部分复制下来。
}
InfixExpre[j+1] = '0';
for(t = (j+1); Exptemp[t] != '\0'; t++)
{
InfixExpre[t+1] = Exptemp[t];
}
}
}
}
/**运算符优先级的比较*/
bool CompareOperator(char x, char y)
{
if(y == '(') //如果 y 是 '(' ,为较高优先级,直接压栈。
{
return 1;
}
else if((y == '*' || y == '/' || y == '%') && (x == '+' || x == '-' || x == '(')) // '*', '/', '%' 的优先级高于 '+', '-', '('
{
return 1;
}
else if((y == '+' || y == '-') && x == '(') // '+', '-' 的优先级高于 '('
{
return 1;
}
else
{
return 0;
}
}
/**中缀表达式转换成后缀表达式*/
void TransformExpression(Stack<char> Operators, char *InfixExpre, char *SuffixExpre)
{
int i ,j;
i = j = 0;
for(i = 0; InfixExpre[i] != '\0'; i++)
{
if((InfixExpre[i] >= '0') && (InfixExpre[i] <= '9') || InfixExpre[i] == '.')
{
SuffixExpre[j] = InfixExpre[i]; //当前读取字符为字符0-9或是小数点时,直接输出。
j++;
}
else
{
if((i != 0) && (InfixExpre[i-1] >= '0') && (InfixExpre[i-1] <= '9'))
{
SuffixExpre[j++] = ' '; //用空格将操作数分隔
}
//如果操作符为 '+', '-', '*', '/', '%'
if((InfixExpre[i] == '+') || (InfixExpre[i] == '-') || (InfixExpre[i] == '*') || (InfixExpre[i] == '/') || (InfixExpre[i] == '%'))
{
//如果操作符栈为空,当前操作符直接入栈。
if(Operators.empty())
{
Operators.Push(InfixExpre[i]);
}
//如果当前操作符优先级 > 栈顶元素优先级,入栈。
else if(CompareOperator(Operators.Top(), InfixExpre[i]) == 1)
{
Operators.Push(InfixExpre[i]);
//printf("%c\n", S.operation[S.top]);
}
//如果当前操作符优先级 < 栈顶元素优先级,栈顶操作符出栈。
else if(CompareOperator(Operators.Top(), InfixExpre[i]) == 0)
{
SuffixExpre[j] = Operators.Pop();
j++;
i--; //下一次循环继续判断当前操作符。
}
}
//如果操作符是 '(' 或是 ')'.
else
{
//如果是'(',直接入栈。
if(InfixExpre[i] == '(')
{
Operators.Push(InfixExpre[i]);
}
//如果栈非空,当前操作符是')',栈顶操作符出栈,直至遇到'('
else if(!Operators.empty() && InfixExpre[i] == ')')
{
while(Operators.Top() != '(')
{
SuffixExpre[j++] = Operators.Pop();
}
Operators.Pop(); // 从栈中删除左括号。
}
}
}
}
while(!Operators.empty()) //遍历结束,如果栈非空,弹出栈中所有的运算符。
{
SuffixExpre[j++] = ' ';
SuffixExpre[j++] = Operators.Pop();
}
SuffixExpre[j++] = '\0';
}
/**对后缀表达式进行预处理*/
/**将字符串中的小数,多位数部分分离出来*/
void StringToFloat(Stack<float> Numbers, char *SuffixExpre)
{
int i, j, k, t, keys[MAXLENGTH];
char Tempsuffix[MAXLENGTH], strnum[MAXLENGTH][MAXLENGTH];
float OperatorNumbers[MAXLENGTH], key;
strcpy(Tempsuffix, SuffixExpre);
i = j = k = t = 0;
while(Tempsuffix[i] != '\0') //遍历后缀表达式字符串
{
if((Tempsuffix[i] >= '0' && Tempsuffix[i] <= '9') || Tempsuffix[i] == '.') //当前字符为字符0-9或者小数点时,保存至数组trnum[j]
{
strnum[j][k++] = Tempsuffix[i];
}
else if(Tempsuffix[i-1] >= '0' && Tempsuffix[i-1] <= '9' && Tempsuffix[i] == ' ')//数字字符后遇到空格,表明该数字读取结束
{
SuffixExpre[t++] = '0'; //中缀表达式中原来为数字的位置用字符'0'替代。
key = atof(strnum[j]); //调用STL库函数将字符串转化为浮点数
OperatorNumbers[j++] = key;
k = 0;
}
else if(Tempsuffix[i] == '+' || Tempsuffix[i] == '-' || Tempsuffix[i] == '*' || Tempsuffix[i] == '/' || Tempsuffix[i] == '%')
{
SuffixExpre[t++] = Tempsuffix[i]; //遇到运算符,直接写入后缀表达式,不做改动。
}
i++;
}
SuffixExpre[t++] = '\0'; //后缀表达式处理完毕,补'\0'
DealSuffix(Numbers, SuffixExpre, OperatorNumbers); //调用函数处理后缀表达式
}
/**计算后缀表达式*/
void DealSuffix(Stack<float> Numbers, char *SuffixExpre, float *OperatorNumbers)
{
int i, j = 0;
float number1, number2;
for(i = 0; SuffixExpre[i] != '\0'; i++)
{
if(SuffixExpre[i] == '0') //当前字符为'0',说明该位置为操作数,将Numbers数组中对应的操作数压栈
{
Numbers.Push(OperatorNumbers[j++]);
}
else
{
number2 = Numbers.Pop(); //先从栈顶取数在操作符右侧运算。
number1 = Numbers.Pop();
Numbers.Push(CalculateResult(SuffixExpre[i], number1, number2));//将运算结果压栈。
}
}
printf("\n运算结果为:%f\n", Numbers.Pop());
}
/**计算数值*/
float CalculateResult(char operators, float number1, float number2)
{
switch(operators)
{
case '+':
{
return (number1 + number2);
break;
}
case '-':
{
return (number1 - number2);
break;
}
case '*':
{
return (number1 * number2);
break;
}
case '/':
{
return (number1 / number2);
break;
}
case '%':
{
return ((int)number1 % (int)number2);
break;
}
default:
{
printf("未能正确运算。\n");
}
}
}