最简单的办法是使用字符串。但在下文中可以看到,为了判断在哪里需要加括号,以及避免重复,我们需要从算式中提取一些信息,而从字符串中提取这些信息就很不方便了。于是我们选用树形结构来表示算式。一个算式表示为一棵树,树的内部结点为运算符,叶子结点为变量。例如,a / (b * (c + d)) 这个算式就用如下的树来表示:
树的结点可以用一个类来实现,目前它只需要三个属性:一个字符(运算符或者表示变量的字母),一个左孩子指针,一个右孩子指针。用Python 语言实现如下:
class Node: def __init__(self, ch = None, left = None, right = None): self.ch = ch self.left = left self.right = right
既然开始设计算式的表示方式了,我们就顺便考虑一下怎么输出一个算式,即怎么把一棵树转换成一个字符串。这也是一个递归的过程:
如果根结点的字符是个字母,那么这棵树仅代表一个变量,只需输出相应的字母;否则,这棵树代表一个算式,可以将两棵子树分别转换成字符串后,用根结点的运算符连接起来。
这里有一个比较麻烦的问题,就是两棵子树转换成的字符串,可能需要加括号。我们当然可以不分青红皂白地一律加括号,但这样输出的算式读起来就比较困难。最省括号的方法如下:
如果子树只代表一个变量,那么不加括号;左子树需要加括号的情况为:根的运算符的优先级高于左子树,即根的运算符为乘除,左子树的运算符为加减;右子树需要加括号的情况有三种:根的运算符的优先级高于右子树,即根的运算符为乘除,右子树的运算符为加减;根的运算符为减,右子树的运算符为加减;根的运算符为除,右子树的运算符为乘除。右子树需要加括号的三种情况,可以合并为:根的运算符为除,或者根的运算符为乘减,且右子树的运算符为加减。
由此可以实现 Node 类的 __str__ 方法,这样就可以通过str(root)把一棵树表示的算式输出出来了。
def __str__(self): if self.ch not in '+-*/': return self.ch # 单变量不加括号 left = str(self.left) # 左子树转字符串 right = str(self.right) # 右子树转