实验题目:栈的应用-算术表达式求值
正文
实验环境: Visual C++ 2010
实验目的:
1.掌握栈的定义及实现;
2.掌握利用栈求解算术表达式的方法。
实验内容:
通过修改完善教材中的算法3.4,利用栈来实现算术表达式求值的算法。对算法3.4中调用的几个函数要给出其实现过程:
(1) 函数In©:判断c是否为运算符;
(2) 函数Precede(t1,t2):判断运算符t1和t2的优先级;
(3) 函数Operate(a,theta,b):对a和b进行二元运算theta。
程序运行时,输入合法的算术表达式(中间值及最终结果要在0~9之间,可以包括加减乘除和括号),便可输出相应的计算结果。如下图*:
实验提示:(仅供参考,每个函数的具体实现可以有多种方法,希望有创新)
一:代码及注释部分:
#include<stdio.h>
#include<string.h>
#include<malloc.h>
#include<stdlib.h>
#include<stack>
#include<iostream>
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define MAXSIZE 100
using namespace std;
int i=1; /*定义一个全局变量,在下文中判断是否是运算符中调用; */
int In(char ch){ // 判断输入的是否是运算符;
i++;
switch(ch){
case'+':
case'-':
case'*':
case'/':
case'=':
case'(':
case')':
return OK;
break;
default:
if(i%3==0)
printf("输入格式错误,求出错误结果:"); /*因为这个要多次调用,所以这里制定一个判断错误机制 */
return ERROR;
}
}
char Precede(char a,char b){ // 比较运算符的优先级;
if(b=='+'){
if(a=='('||a=='=') return '<';
return '>';
}
if(b=='-'){
if(a=='('||a=='=') return '<';
return '>';
}
if(b=='*'){
if(a=='*'||a=='/'||a==')') return '>';
return '<';
}
if(b=='/'){
if(a=='*'||a=='/'||a==')') return '>';
return '<';
}
if(b=='(') return '<';
if(b==')'){
if(a=='(') return '=';
return '>';
}
if(b=='='){
if(a=='=') return '=';
return '>';
}
}
double Operate(double a,char theta,double b){ /*把 a、b进行二元运算 ,这里用的是double是因为方便后文做以后的更改,调试想要的结果(小数位数);*/
switch(theta){
case '+':
return a+b;
break;
case '-':
return a-b;
break;
case '*':
return a*b;
break;
case '/':
return a/b;
break;
}
}
double EvaluateExpression(){ // 计算过程;
char str[10010];
stack<double> OPND;
stack<char> OPTR;
scanf("%s",str);
OPTR.push('=');
double a,b,shu=0;
int flag=0;
char theta;
for(int i=0;str[i];i++){
if(In(str[i])==0){
shu=shu*10+str[i]-'0';
flag=1;
}
else{
if(flag){
OPND.push(shu);
shu=0;
flag=0;
}
switch(Precede(OPTR.top(),str[i])){
case '<':
OPTR.push(str[i]);
break;
case '>':
theta=OPTR.top(); OPTR.pop();
b=OPND.top(); OPND.pop();
a=OPND.top(); OPND.pop();
OPND.push(Operate(a,theta,b));
i--;
/*这里是因为判断的该符号 str[i] 还没有入栈,需要回去判断一下 ;*/
break;
case '=':
OPTR.pop();
break;
}
}
}
return OPND.top();
}
int main(){
puts("请输入算术表达式,负数要用(0-正数)表示,并以=结束");
printf("%.0f\n",EvaluateExpression());
/*这里可以按照我们的要求,把0改成想要的数字约束是否是小数,有几个小数 ;*/
system("pause");
return 0;
}
二:流程图
三:实验分析:
1:本实验采取函数相互嵌套设计程序,书写时采用一定的缩进格式使得主函数更加简洁,整体显得美观。
2:本程序针对非法字符会做出一定的反应,即会输出提醒:输入字符错误。具有一定的健壮性。
3:本程序运行时,用户可以根据提示信息进行操作,具有一定的界面友好性。
4:本实验格式不一,具有多种书写方式,有一种更简洁的书写方式。为了程序结构不那么单一,我将这几种书写方式进行了融合,把哪几种书写方式在本程序中都有体现。
5:本实验的健壮性还不是那么完善,由于自身能力不足,在此可能有些非法字符会被判断两次,程序作出两次反应,所以这是程序的缺点之一。
6:由于按照实验要求,要进行整数测试程序。在这里我对程序设计了浮点数兼容的模式,即输入浮点数也可以进行运算,但是最后的输出需要把%.0f改成%f即可。
四:实验程序测试输出:
附录:栈的定义及实现:
#ifndef STACK_H_INCLUDED
#define STACK_H_INCLUDED
#endif // STACK_H_INCLUDED
#define MAXSIZE 100
#include<stdlib.h>
using namespace std;
typedef char SElemType;
typedef struct
{
char *base;
char *top;
int stacksize;
}SqStack;
void InitStack(SqStack &S)
{
S.base=new char [MAXSIZE];
if(!S.base) exit(0);
S.top=S.base;
S.stacksize=MAXSIZE;
}
bool Push(SqStack &S,char e)
{
if(S.top-S.base==S.stacksize) return false;
*S.top++=e;
return true;
}
bool Pop(SqStack &S,char &e)
{
if(S.top==S.base) return false;
e=*--S.top;
//cout<<"pop="<<*S.top;
return true;
}
char GetTop(SqStack S)
{
if(S.top!=S.base)
return *(S.top-1);
}
选做内容1:
进一步改进,使表达式的中间值及最终结果不局限于0~9之间的个位数。(如果完成要在实验报告中注明),如下图:
选做内容2:
将表达式转化成后缀表达式输出,利用后缀表达式求表达式的值并输出。
将中缀表达式转化成后缀表达式存储在队列中,然后利用后缀表达式求表达式的值并输出。
将中缀表达式转化成后缀的思想:
(1)创建一空队列,用来存放后缀表达式,建立并初始化操作符栈OPTR,将表达式起始符“#”压入OPTR栈。
(2)依次读入表达式中每个字符ch,循环执行(3)至(5),直至求出整个表达式转换完毕。
(3)取出OPTR的栈顶元素,当OPTR的栈顶元素和当前读入的字符ch均为“#”时,整个中缀表达式转换完毕。
(4)若ch不是运算符,则进队,读入下一字符ch。
(5)若ch是运算符,则根据OPTR的栈顶元素和ch的优先权比较结果,做不同的处理。
① 若是小于,则ch压入OPTR栈,读入下一字符ch。
② 若是大于,则弹出OPTR栈顶的运算符,进队。
③ 若是等于,则OPTR的栈顶元素是“(”且ch是“)”,这时弹出OPTR栈顶的“(”,相当于去掉括号,然后读入下一字符ch。
对后缀表达式进行计算的具体步骤为:
建立一个栈S从左到右读后缀表达式,读到数字就将它转换为数值压入栈S中,读到运算符则从栈中依次弹出两个数分别到Y和X,然后以“X运算符Y”的形式计算出结果,再压进栈S中。如果后缀表达式未读完,重复执行上面过程,最后输出栈顶的数值即可结束。
实验要求:
(1) 程序要添加适当的注释,程序的书写要采用缩进格式。
(2) 程序要具在一定的健壮性,即当输入数据非法时,程序也能适当地做出反应。
(3) 程序要做到界面友好,在程序运行时用户可以根据相应的提示信息进行操作。
(4) 根据实验报告模板详细书写实验报告,在实验报告中给出表达式求值算法的流程图。
(5) 将栈的定义和实现单独保存在一个头文件“stack.h”中,表达式求值的源程序保存为“calculator.cpp”,实验报告命名为“实验报告2.doc”。将stack.h、calculator.cpp和“实验报告2.doc”三个文件压缩为一个文件,按以下方式命名:学号姓名.rar,上传到移动教学平台。*