栈设计表达式求值设计文档格式.docx
《栈设计表达式求值设计文档格式.docx》由会员分享,可在线阅读,更多相关《栈设计表达式求值设计文档格式.docx(24页珍藏版)》请在冰豆网上搜索。
![栈设计表达式求值设计文档格式.docx](https://file1.bdocx.com/fileroot1/2022-10/9/c3e5d773-9162-40c6-bce5-2e2fbed6c4b6/c3e5d773-9162-40c6-bce5-2e2fbed6c4b61.gif)
2.1后缀表达式设计
后缀表达式是由一系列的运算符、操作数组成的表达式,其中运算符位于两个操作数之后。
后缀表达式很容易通过应用栈实现表达式的计算。
其基本过程是:
当输入一个操作数时,它被压入栈中,当一个运算符出现时,就从栈中弹出适当数量的操作数,对该运算进行计算,计算结果再压回栈中。
对于最常见的二元运算符来说,弹出的操作数只有两个。
处理完整个后缀表达式之后,栈顶上的元素就是表达式的结果值。
整个计算过程不需要理解计算的优先级规则。
2.2中缀到后缀的转换设计
从上面的分析可知,后缀表达式是很容易应用栈进行计算的,但要处理的是中缀表达式。
同样,也可以应用栈将中缀表达式转换为后缀表达式。
此时,栈里要保存的是运算符,而在后缀表达式计算中,栈里保存的是操作数。
应用栈将中缀表达式转换为后缀表达式的基本过程如下。
从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理:
●如果遇到空格,则认为是分隔符,不需处理。
●若遇到操作数,则直接输出。
●若是左括号,则将其压入至栈中。
●若遇到的是右括号,表明括号的中缀表达式已经扫描完毕,把括号中的运算符退栈,并输出。
●若遇到的是运算符,当该运算符的优先级大于栈顶运算符的优先级时,则把它压栈,当该运算符的优先级小于等于栈顶运算符时,将栈顶运算符退栈并输出,再比较新的栈顶运算符,按同样处理方法,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈。
●若中缀表达式处理完毕,则要把栈中存留的运算符一并输出。
上述处理过程的一个关键是不同运算符优先级的设置。
在程序实现中,可以用一个数来代表运算符的优先级,优先级数值越大,它的优先级越高,这样的优先级的比较就转换为两个数的大小比较。
程序的整体算法过程分两步:
第一步,从“input.txt”文件中读取中缀表达式,并应用运算符栈OpHolder把中缀表达式转换为后缀表达式,将输出结果(转换后得到的后缀表达式)存放在一个temp文件中。
第二步,从temp文件中读取后缀表达式,并应用操作数栈Operands计算后缀表达式结果,将结果输出到“output.txt”文件中。
本程序中的栈采用前面所述的带头节点的链式存储结构,涉及两种类型:
用于存储运算符号的char类型的链栈以及用于存储操作数的float类型的链栈。
本程序将整个求值过程分解为两个步骤:
中缀表达式转换为后缀表达式、计算后缀表达式结果值。
其实,可以将这两个过程统一合并在一起完成,当然也同样需要操作数和运算符这两类栈。
另外,本程序中,应用函数OperatorValue来判别运算符的优先级,一种更灵活的方式是应用数组来记录各运算符的优先级。
读者可应用以上思路对本程序进一步改进。
3设计实践
3.1实现要求
本程序的输入形式是“input.txt”文件,输出结果存放到“output.txt”文件中。
在输入文件中等式的格式必须是中缀格式,例如1+2*3,而且每一行只允许一个表达式。
本程序将读入的中缀表达式转换为后缀表达式,并存放在temp,txt文件中;
随后从temp.txt中读取后缀表达式,并将计算结果输入到output.txt中。
一个char类型的栈“Whereat”用来记录后缀表达式中操作数和运算符号的顺序,以决定需要多少次计算。
3.2中缀表达式求值
中缀表达式:
每个二目运算符在两个运算量的中间,假设操作数是整型常数,运算符只含加、减、乘、除等四种运算符,界限符有左右括号和表达式起始、结束符“#”,如:
#(7+15)*(23-28/4)#。
要对一个简单的算术表达式求值,首先要了解算术四则运算的规则,即:
(1)从左到右;
(2)先乘除,后加减;
(3)先括号内,后括号外。
运算符和界限符可统称为算符,它们构成的集合命名为OPS。
根据上述三条运算规则,在运算过程中,任意两个前后相继出现的算符θ1和θ2之间的优先关系必为下面三种关系之一:
θ1<
θ2,θ1的优先权低于θ2。
θ1=θ2,θ1的优先权等于θ2。
θ1>
θ2,θ1的优先权高于θ2。
实现算符优先算法时需要使用两个工作栈:
一个称作operator,用以存放运算符;
另一个称作operand,用以存放操作数或运算的中间结果。
算法的基本过程如下:
首先初始化操作数栈operand和运算符栈operator,并将表达式起始符“#”压入运算符栈;
依次读入表达式中的每个字符,若是操作数则直接进入操作数栈operand,若是运算符,则与运算符栈operator的栈顶运算符进行优先权比较,并做如下处理:
(1)若栈顶运算符的优先级低于刚读入的运算符,则让刚读入的运算符进operator栈;
(2)若栈顶运算符的优先级高于刚读入的运算符,则将栈顶运算符退栈,送入θ,同时将操作数栈operand退栈两次,得到两个操作数a、b,对a、b进行θ运算后,将运算结果作为中间结果推入operand栈;
(3)若栈顶运算符的优先级与刚读入的运算符的优先级相同,说明左右括号相遇,只需将栈顶运算符(左括号)退栈即可。
operator栈的栈顶元素和当前读入的字符均为“#”时,说明表达式起始符“#”与表达式结束符“#”相遇,整个表达式求解结束。
3.3后缀表达式求值
计算一个后缀表达式,算法上比计算一个中缀表达式简单的多。
这是因为表达式中即无括号又无优先级的约束。
具体做法:
只使用一个对象栈,当从左向右扫描表达式时,每遇到一个操作数就送入栈中保存,每遇到一个运算符就从栈中取出两个操作数进行当前的计算,然后把结果再入栈,直到整个表达式结束,这时送入栈顶的值就是结果。
下面是后缀表达式求值的算法,在下面的算法中假设,每个表达式是合乎语法的,并且假设后缀表达式已被存入一个足够大的字符数组A中,且以‘#’为结束字符,为了简化问题,限定运算数的位数仅为一位且忽略了数字字符串与相对应的数据之间的转换问题。
3.4中缀表达式转换成后缀表达式
将中缀表达式转化为后缀表达式和前述对中缀表达式求值的方法完全类似,但只需要运算符栈,遇到运算对象时直接放后缀表达式的存储区,假设中缀表达式本身合法且在字符数组A中,转换后的后缀表达式存储在字符数组B中。
遇到运算对象顺序向存储后缀表达式的B数组中存放,遇到运算符时类似于中缀表达式求值时对运算符的处理过程,但运算符出栈后不是进行相应的运算,而是将其送入B中存放,程序的整体算法分两步:
第一步,从”input.txt”文件中读取中缀表达式,并应用运算符栈OpHolder把中缀表达式转换为后缀表达式,将输出结果存放在一个temp文件中。
第二步,从temp文件中读取中缀表达式,并应用操作栈Operands计算后缀表达式结果,将结果输出到”output.txt”文件中。
4测试方法
测试项目
测试实例
期望测试结果
基本测试
3.00
1+2+3-4
2.00
1+2
4.99+5.99+6.99*1.06
18.39
(3*5*(4+8)%2)
0.00
2^2^3
256.00
2^2.5^3
50535.16
(2-4)^3
-8.00
扩展测试
2.5-3.4/2+1*2
2.80
(2.5)*(3-2)+5.6-190%3^2^(1+1)
-19.90
1+2+3
6.00
1*2*3
(1+2)*3+4/(5+1*4)+3.26
12.70
3+4-6.7+8
8.30
2.9*1.2+0.5-4%3/2+4^(5-5)
4.48
2^3-(5+2)/7*9
-1.00
50-3^2^2+4%2-7*(3)
-52.00
((
(2))-3)
2.5^2
6.25
2^(4.4-2.4)
4.00
2^(4.4-3.1)
2.46
(2-4)^(5-8)
-0.13
容错测试
1+2(
ERRORININFIXNOTATION
2/0
2%0
(2-4)^3.1
2.5%2
2%2.5
2+3)(-5
((
(2))-3))
(((
(2))-3)
3.5/(1-1)
(5.6-2)%3
5%(3.2-2.1)
3.0.2+1
1+++1
1#1
22
+-+
5程序运行效果
输入如下:
输出如下:
6设计心得
通过这次课程设计,增加了我对数据结构知识的了解。
在具体操作中,也发现自己的不足之处,对于所学知识的理解还不够深刻,运用起来也不够熟练。
发现上机调试的重要作用,特别是对对栈处理的基本算法如判空、压栈、弹栈、栈元素倒置等知识的运用有了有了更深的理解。
通过实际操作,学会利用数据结构进行编程的基本思想与方法同时也了解了实验报告撰写的基本要求。
回顾起此次课程设计,从拿到题目到完成整个编程,从理论到实践,在这些日子里,可以学到很多很多的东西,同时不仅可以巩固了以前所学过的知识,而且学到了很多在书本上所没有学到过的知识。
同时,我们不能只局限于将书本上的代码看懂,会调试运行,我们要善于发现问题,提出自己的想法,联系实际生活,开拓思维,对程序进行改进和完善。
我们要学会通过网络、书籍等多途径。
通过这次课程设计使我们懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论。
7附录
#include<
stdio.h>
stdlib.h>
math.h>
intPrintError=0;
/*全局变量,0代表正常,1代表表达式出错*/
/*char类型链表式堆栈,用来存放运算符号,以及用在中缀表达式转换等时候*/
typedefstructNode*PtrToNode;
typedefPtrToNodeStack;
intIsEmpty(StackS);
voidMakeEmpty(StackS);