题目:写一个能自动生成小学四则运算题目程序
要求:
- 除了整数以外,还要支持真分数的四则运算
- 让程序能接受用户输入答案,并判断对错
对于求解四则运算的算法,在数据结构课程上有过相同的题目,但是当时我们需要做的求一个给定的四则运算题目的结果。根据逆波兰表达式将中缀式转换成后缀表达式再利用栈结构求解。这次的任务难点确在于随机生成四则运算题目。刚开始的想法太low了,根据常规表达式的结构随机生成一个数,一个运算符,再一个数……但是往深了想,这种方法很难实现,因为一个表达式不仅仅有操作数和+ - * / 运算符,还有括号这个最高优先级的运算符,往一个随机生成的操作数和四个运算符组成的符号流中添括号,想想好复杂。所以采用了二叉树结构,非终结点为运算符,叶子结点为操作数,形如:a+b,a*(b+c),(a-b)*c/d可以表示为:
这种结构抽象为:
很自然就能想到递归结构,接下来就是具体实现地过程了。
先定义表达式结点类Experssion:常规属性为父节点指针Parent,左子结点指针Left,右子结点指针Right,还包括表达式结果Result,当前运算符oper,是否为左节点标志isLeft(为了后来添加括号而定义)。
其中,为了支持分数运算,我后来又定义一个Value类,私有数据为分子,分母(int numer,int demon),这样可以将整数统一看成分母为1的分数进行运算。类中成员函数包括的当然就是加减乘除和比较运算符的重载函数了,但是题目要求将分数显示为最简真分数的形式,所以又定义了约分函数Gcd。不过在实现开始阶段,我将数据类型就用整型代替了,这样比较方便。
生成一个表达式可以分成三步:创建表达式框架,根据生成的框架填充表达式和为表达式添加括号。
1)创建表达式框架:Void CreateEx(int NumOfOper)
其中, NumOfOper表示为即将生成的表达式中的运算符个数,这决定了表达式的基本框架。
整个二叉树的根节点是一个非终结点,表示的是一个运算符,当然这个运算符是随机生成的。接下来就是为根节点的左子结点和右子结点分配运算符个数了
1 int NumOfLeftOper = rand() % NumOfOper; 2 int NumOfRightOper = NumOfOper - NumOfLeftOper-1; 3 left->CreateEx(NumOfLeftOper); 4 right->CreateEx(NumOfRightOper);
递归生成左子式和右子式,直到运算符个数分配完,基本框架也就形成了。
2)根据生成的框架填充表达式:Value FillExFrame(int MaxNum)
其中,MaxNum表示的是题目要求的表达式中操作数的最大值。
采用的也是递归求解。
LResult = left->FillExFrame(MaxNum); RResult = right->FillExFrame(MaxNum);
递归深入到叶子结点时,随机生成操作数。但是其中有很多细节问题需要注意,题目要求有:生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1 − e2的子表达式,那么e1 ≥ e2。生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。这也就说操作数的随机生成也是有限制的。根据运算符的不同,LResult和RResult要分情况讨论(其中,LResult和RResult表示左子表达式结果和右子表达式结果)。当oper为 ‘-‘ 时,LResult>=RResult。当oper为 ‘÷‘ 时,RResult != 0 && LResult <= RResult。但是根据运行程序,叶子结点在最底层被生成,这两个叶子结点只能符合直系父节点运算符的要求,在父节点中根据这两个叶子结点和运算符计算出来的结果并不一定能符合更上一层结点运算符的要求,在这种情况下该怎么办呢?我在这里偷了点懒,当发生oper为 ‘-’ 号,但是LResult < RResult时,就将这个运算符变成 ‘÷’ 号。当oper为 ‘÷‘ 号,RResult == 0 或LResult > RResult 时,就将oper变成 ‘-‘ 号,这样就不用考虑其他的问题了(⊙﹏⊙b汗)
到这里,框架也填充完整了。
3)为表达式添加括号
这就要考虑运算符和括号之间优先级了,一开始定义的isLeft就是这时候用的,子表达式是当前左子结点时,同级运算符并不需要加括号的(同级运算符先算左边)。当子表达式是当前右子节点时,同级运算符就需要加括号了,也就这点区别,其他的情况当然是低优先级就加括号了。
基本上,表达式类就形成了,把表达式中操作数的数据类型变成已经定义的Value类,就基本完成了整个程序。
后来,我有把这个程序添加了图形界面,用的是MFC,就是一个简单的列表控件显示表达式,这个控件就是上一篇博客中的可编辑列表框。运行结果如下图: