目录
一、实验目的
二、实验内容
三、实验提示
四、实验思路
1.整体思路
2.详细思路
五、实验思考过程
六、代码运行截图
1.个位数的运算
2. 多位数的运算
3.非法输入的情况
七、程序流程图
八、个人收获及实验总结
1.个人收获
2.实验总结
九、实验源代码
1、掌握栈与队列的定义;
2、掌握掌握栈与队列的基本操作。
1.验证某算术表达式的正确性,若正确,则计算该算术表达式的值。
(1)、从键盘上输入表达式的值。
(2)、分析该表达式是否合法:
(a)是数字,则 判断该数字的合法性。若合法,则压入数据存放堆栈。
(b)是规定的运算符,则根据规则进行处理。在处理过程中,将计算该表达式的值。
(c)若是其它字符,则返回错误信息。
(3)、若上述处理过程中没有发现错误,则认为该表达式合法,并打印处理结果。
表达式的计算过程:
1、变量直接输出;
2、遇到“(”,无条件入栈;
3、输入运算优先数是2,3,4,5,时,如栈空则入栈,否则,将其与栈顶元素进行比较,如果优先级大,就进栈,否则就退栈输出;
4、如遇“)”,同时栈顶元为“(”,则退栈,抹去括号。
首先将用户输入一整串的运算式存入字符exp数组,然后通过栈进行一系列处理得到运算结果。
(1)输入运算式,定义并初始化数字栈和字符栈分别用于存储输入数据中的数字和运算符。
(2)若为数字,则压入数字栈,此处与众不同的是通过归并的思想,可实现多位数的运算。
(3)若为运算符,则首先进行与栈顶元素优先级的判断。若优先级低于栈顶元素,则运算符栈栈顶元素弹出,同时弹出数字栈两个栈顶元素进行运算。得到的运算结果压入数字栈;若优先级高于栈顶元素,则直接入栈;若优先级相等,则栈顶元素出栈。
(4)若为空格,则直接跳过。
(5)若为其他情况,则判为非法字符输入,程序停止运行。
(6)经过一系列的运算、出栈、入栈的操作后,最终获取数字栈中的数据可得运算结果。
此次实验代码,经过反复思考,通过三次更改,得到了最后的版本。
5.此程序总体功能:可实现多位整数的运算,但由于时间有限,尚未想出浮点数的求值算法。
6.现版本输入方式的选取:整体输入。此方式可通过直接建立一个字符数组利用gets方法或scanf函数获取输入的数据。逐个输入则需要使用getchar()进行逐个读取。整体输入相对逐个输入更加直观。
7.优先级判断函数的思路:在前两个版本中,我采用的是比较直白的if语句,列出可能的情况进行结果输出。现版本受网上资料的启发,采用以二维数组存储优先列表,将可能的7*7种情况列出,利用switch语句,输出对应的结果,更加直观。
8.提高程序健壮性的体现:
(1)整个程序运算中,无可避免的出现了除法运算,因此在求值函数operate中,当theta为“/”,利用if语句判断,若除数为0,则提示用户输入错误并结束程序。
(2)运算式输入的字符串出现非法字符、空格的处理:在evaluateExpression函数中,同样采用if语句进行判断,若为空格,则使用continue跳过,若不为字符、数字和空格,则输入为非法字符,程序将提示输入错误。
(3)在对栈操作方面的体现:初始化栈时对内存是否申请成功进行判断、入栈时对是否栈满进行判断、出栈和获取栈顶元素时对是否栈空进行判断。
9.程序运行界面友好的体现:输入输出方面进行一系列的提示,以输入为更重要,受程序算法限制,输入的运算式必须以“#”结尾,否则无法准确的识别。
(1)通过此次实验对栈这一数据结构有了更深一步的认识,也更加熟悉了栈的一系列表示和实现操作。
(2)通过表达式求值方法的探索,深入理解了“后缀表达式”的含义,并掌握了将中缀表达式转化为后缀表达式的方法,这同样也是表达式求值的重要算法。
(3)在功能扩展方面,将个位数的运算提升到多位数的运算,很好的学习并利用了归并的思想,其与递归思想有很多类似之处。
(4)对C语言和C++两种编程语言差别的认识又多了一个细微的收获:
最初采用的是这种函数参数,即函数参数带&符号,与教材上的伪代码一致。但在实际运行时Xcode编译器却一直对此处报错(结果如下)。查阅资料后发现,&为引用符,它是C++的内容,当需要对原变量进行修改时,可以给参数添加&。但部分C语言编译器是无法适用的。
问题的解决:思考后发现,这种函数参数加&的功能与C语言指针的功能十分相似,因此将函数参数定义为指针类型即可很好的解决此问题;另一方面,也可以直接将文件后缀名从.c
改为.cpp,即改成C++文件,同样可以解决此问题。
表达式求值这一算法核心在于对运算式的运算过程,利用栈来实现是很方便的。因此便涉及运算符优先级的判断和如何对进行有序的运算的思考。此外,对功能是否可以扩展的思考同样极为重要,其可大大提高程序的实
#include
#include
#include
//利用宏常量定义最大存储空间
#define Maxsize 100
//定义数字栈
typedef struct Stack
{
int *top;
int *base;
int stacksize;
}sqstack;
//构造空栈
void initStack(sqstack *s)
{
//申请内存空间
s->base=(int *)malloc(Maxsize*sizeof(int));
if(!s->base)
{
printf("栈初始化失败!\n");
return;
}
//初始化,此时栈空
s->top=s->base;
s->stacksize=Maxsize;
return;
}
//入栈操作
int push(sqstack *s,int e)
{
if(s->top-s->base==s->stacksize)
{
printf("栈满!\n");
return 0;
}
*s->top++=e;
return 1;
}
//出栈操作
int pop(sqstack *s,int *elem)
{
if(s->base==s->top)
{
printf("栈空!\n");
return 0;
}
*elem=*--s->top;
return 1;
}
//获取栈顶元素
int getTop(sqstack *s)
{
if(s->base==s->top)
{
printf("栈空!无法获取元素!\n");
return 0;
}
return *(s->top-1);
}
//判断运算符优先级
char Precede(char theta1,char theta2)
{
int i = 0,j = 0;
//对7*7种可能性进行分列,得到最后的运算符对比结果
char pre[7][7]={// + - * / ( ) =
{'>','>','<','<','<','>','>'},
{'>','>','<','<','<','>','>'},
{'>','>','>','>','<','>','>'},
{'>','>','>','>','<','>','>'},
{'<','<','<','<','<','=','0'},
{'>','>','>','>','0','>','>'},
{'<','<','<','<','<','0','='}};
switch(theta1){
case '+': i=0; break;
case '-': i=1; break;
case '*': i=2; break;
case '/': i=3; break;
case '(': i=4; break;
case ')': i=5; break;
case '=': i=6; break;
}
switch(theta2){
case '+': j=0; break;
case '-': j=1; break;
case '*': j=2; break;
case '/': j=3; break;
case '(': j=4; break;
case ')': j=5; break;
case '=': j=6; break;
}
return(pre[i][j]);
}
//对操作数进行运算
int Operate(int a,char theta,int b)
{
switch(theta){
case'+':return a+b;
case'-':return a-b;
case'*':return a*b;
case'/':
//考虑分母为0的情况,若除数为零返回错误提示
if(b!=0)
return a/b;
else
{
printf("分母为0,无意义!\n");
exit(0);
}
}
return 0;
}
//判断是否为运算符,若是运算符返回1,若不是返回0
int isChar(char c)
{
switch(c){
case '+':
case '-':
case '*':
case '/':
case '(':
case ')':
case '=':
return 1;
default:
return 0;
}
}
//核心算法,进行运算式运算
int evaluateExpression(char exp[])
{
//定义运算数栈、运算符栈
sqstack OPND,OPTR;
/* a,b,theta用于Operate函数
X用于存放多余的出栈字符
X1,X2用于归并 */
int a,b;
int x,X1,X2,theta;
//读取字符的变量
char ch;
//指向存放表达式数组的下标指针(其实不是真正的指针,而是数组下标)
int i=0;
//建立并初始化运算数栈OPND
initStack(&OPND);
//建立并初始化运算符栈OPTR
initStack(&OPTR);
//先将“=”号压入OPTR栈(表达式也必须以“=”结束)
push(&OPTR,'=');
//ch读取字符数组第一个元素,并将指针i后移一位
ch=exp[i++];
//表达式没有扫描完毕或OPTR的栈顶不为“=”
while(ch!='='||getTop(&OPTR)!='=')
{
if(isChar(ch)) //判断ch是否为操作符
{
//比较ch的与OPTR栈顶元素的优先级
switch(Precede(getTop(&OPTR),ch))
{
case'<':
push(&OPTR,ch);
//读取下一位字符并将指针向后偏移一位
ch=exp[i++];
break;
case'>':
/*若栈顶操作符优先级更大,则弹出运算符栈顶运算符及数字栈栈顶两个运算数
*/
pop(&OPTR,&theta);
pop(&OPND,&b);
pop(&OPND,&a);
//运算结果压入数字栈
push(&OPND,Operate(a,theta,b));
break;
case'=':
//若优先级相当,则将运算符栈栈顶元素弹出
pop(&OPTR,&x);
//读取下一位字符并将指针向后偏移一位
ch=exp[i++];
break;
}
}
//判断是否为数字字符
else if(isdigit(ch))
{
//将字符形式转化为数字
X1=ch-'0';
push(&OPND,X1);
X2=X1;
//读取下一位字符并将指针向后偏移一位
ch=exp[i++];
//归并思想,实现多位数运算
while(isdigit(ch)) //判断下一位是否还是数字
{
X1=ch-'0';
X2=10*X2+X1; //归并至X2
pop(&OPND,&x);
push(&OPND,X2);
ch=exp[i++]; //读取下一位字符并将指针向后偏移一位
}
}
else if(ch==' ') //判断是否为空格
{
while(ch==' ') //跳过若干个空格
{
ch=exp[i++]; //忽略空格,直接取下一位
}
}
else //若不是上述三种情况之一,则为非法字符
{
printf("Input has illegal characters!\n");
printf("Please enter again.\n");
exit(0); //返回错误提示
}
}
return(getTop(&OPND)); //最后返回操作数栈顶为运算结果
}
int main()
{
char exp[100]; //定义一个字符数组用于存储表达式
int result;
printf("请输入运算式(运算式尾部务必加上‘=’):\n");
gets(exp);
result=evaluateExpression(exp);
printf("运算式及运算结果如下:\n");
printf("%s%d\n",exp,result);
return 0;
}
分享结束,感谢观看!!!