前言
前、后缀表达式的基本含义可见本人的文章:传送门,本处不再赘述,表达式树在文中也有图示。针对只含有二元运算符的表达式树,树中所有的叶节点均为操作数,非叶节点均为操作符。前后缀表达式可以通过不同的遍历策略遍历表达式树得到,而通过前后缀表达式也可以重建得到表达式树,重建过程和二叉树的序列化与反序列化非常类似,由于操作树本身带有是叶子节点(无子节点)这一信息并且已知整棵树的根节点位置(中缀就无法直接得出根节点位置),因此可以直接根据表达式进行重建,各表达式的具体重建过程分析及代码实现见下文。
先声明本文将用到的基本数据结构:
1 | enum OperType |
前缀表达式重建表达式树
由于二叉树是递归定义的,因此自然而然的思路就是尝试使用递归过程来重建二叉树,同时考虑到遍历过程也能通过辅助数据结构变成非递归过程,此时也可以考虑尝试使用非递归来重建,具体递归与非递归实现见下两小节。
前缀重建树的递归实现
由于前缀表达式的第一个节点是根节点,如果节点存在子节点,则紧邻其后的为其左子节点,否则需要递归返回,即本处递归终止条件是遇到操作数节点(因为操作数是没有子节点的)或者整个表达式重建完毕,具体实现思路可见代码及注释,代码如下:
1 |
|
前缀重建树的非递归实现
显然,如果需要不使用递归,则必须使用栈这种结构来保存中间结果。考虑到前序遍历的结果是根->左->右,因此我们可以反向遍历表达式,在遇到操作符时可以弹出栈中两个操作数(子表达式树看作一个操作数)和该操作符组成一颗子表达式树并把子表达式树根节点压入栈中,不断重复该过程直到栈中只剩下一个节点即为最终结果,具体实现可见如下代码:
1 |
|
前缀重建树代码测试
考虑使用本人前面测试用的表达式树,树的形态如下图所示:图片来源
1 |
|
后缀表达式重建表达式树
后缀表达式和前缀表达式的区别是后缀表达式的操作符是在操作数的后面,即左->右->根,因此后缀重建的递归算法可以参考前缀表达式重建算法,只需稍微改变遍历顺序即可。具体代码可见如下两小节。
后缀重建树的递归实现
正如前面所说,后缀前缀表达式不同在于根节点位置,对于后缀表达式根节点在最后,因此参考前缀表达式重建树的递归算法只需从后往前递归即可,基本实现代码如下所示:
1 |
|
后缀重建树的非递归实现
直接把后缀计算的代码稍作修改即可,代码如下所示:
1 |
|
后缀重建树代码测试
依旧使用前缀图中的表达式树,测试代码如下:
1 |
|