Preorder and inorder sequences of binary trees
题目
We consider binary trees with nodes that are identified by single lower-case letters, as in the example of problem 4.16.
a) Write predicates preorder/2 and inorder/2 that construct the preorder and inorder sequence of a given binary tree, respectively. The results should be atoms, e.g. 'abdecfg' for the preorder sequence of the example in problem 4.16.
b) Can you use preorder/2 from problem part a) in the reverse direction; i.e. given a preorder sequence, construct a corresponding tree? If not, make the necessary arrangements.
c) If both the preorder sequence and the inorder sequence of the nodes of a binary tree are given, then the tree is determined unambiguously. Write a predicate pre_in_tree/3 that does the job.
d) Solve problems a) to c) using difference lists. Cool! Use the predefined predicate time/1 to compare the solutions.
What happens if the same character appears in more than one node. Try for instance pre_in_tree(aba,baa,T).
解题思路
这个题目包含四个问题:
- 前序遍历和中序遍历二叉树并按遍历顺序输出节点值
- 从前序和中序遍历得来的节点序列分别重构二叉树
- 从前序和中序遍历序列重构唯一的二叉树
- 使用difference list解决第三个问题
前序遍历和中序遍历二叉树并按遍历顺序输出节点值
前序遍历
前序遍历的规则是:
- 先访问自身节点
- 再访问左子树
- 再访问右子树
举个例子,给定二叉树[a,[b,None,None],[c,None,None]]
:
- 先访问根节点
a
- 再访问左子树
[b,None,None]
- 再访问右子树
[c,None,None]
结果得到遍历序列abc
。
代码实现:
单元测试:
中序遍历
中序遍历的规则是:
- 先访问左子树
- 再访问自身节点
- 再访问右子树
举个例子,给定二叉树[a,[b,None,None],[c,None,None]]
:
- 先访问左子树
[b,None,None]
- 再访问自身节点
a
- 再访问右子树
[c,None,None]
结果得到遍历序列bac
。
代码实现:
单元测试:
从前序和中序遍历得来的节点序列分别重构二叉树
从前序遍历字符序列重构二叉树
从前序遍历序列无法重构唯一的二叉树。对于一个前序遍历序列,其左右子树有多种可能。比如,前序遍历序列abc
,其左右子树序列的组合有:
`和
bc`b
和c
bc
和``
对于同一子树序列,也可能由多个不同子树生成。縂之,对长度大于1
的序列,可重构出多个二叉树。
重构过程先把序列拆成自身节点、左子树序列和右子树序列。自身节点拆分法是唯一的,而左右子树序列的拆分法却不是唯一。且每一种子树序列拆分法重构出来的子树也不是唯一的。 这𥚃有两个「不唯一」,字符序列拆分和从字符序列重构二叉树。结果数量就在这两个地方进行了扩展,扩展方式就是完全组合。
代码实现:
拆分字符序列的时候罗列了所有组合。
for i in range(0, len(s)+1):
leftAndRights.append([s[:i], s[i:]])
再针对每一组拆分组合,构造所有可能的左右子树,再罗列所有的左右子树组合,得到所有可能的重构二叉树。 二叉树是递归结构,所以重构过程也是递归的。每一层都要拆分字符序列、构造左右子树、罗列所有左右子树组合。
从中序遍历字符序列重构二叉树
从中序遍历字符序列无法重构出唯一的二叉树。对于一个序列,其左子树、自身节点和右子树有多种拆分方法。比如,序列abc
,其可以有以下拆分:
- 左子树
`,自身节点
a,右子树
bc` - 左子树
a
,自身节点b
,右子树c
- 左子树
ab
,自身节点c
,右子树``
对于同一子树序列,也可能由多个不同子树生成。縂之,对长度大于1
的序列,可重构出多个二叉树。
重构过程是先把序列拆分为左子树序列、自身节点和右子树序列,再分别构造左右子树。左右子树也是二叉树,所以套用对整树相同的方法。序列拆分会生成多个结果,针对同一个序列构造子树也会生成多个结果。这两个结果集再组合就得到了最终结果,所有符合中序遍历序的的二叉树。
代码实现:
拆分序的方法有多种,这𥚃就罗列了所有的:
for i in range(0, len(s)):
leftAndEAndRights.append([s[:i], s[i], s[i+1:]])
针对每一种序列,重构的二叉树也有多种。且左右子树的组合方式也有多种。
for (l, e, r) in leftAndEAndRights:
result = result+[[e, left, right]
for left in inorder_to_tree(l) for right in inorder_to_tree(r)]
从前序和中序遍历序列重构唯一的二叉树
当同时给定前序遍历和中序遍历序列时,可以重构出唯一的二叉树。
先从前序遍历序列中重构出所有的二叉树‧再从中序遍历序列中重构出所有的二叉树。最后最两个结果集的交集即唯一的二叉树。
代码实现:
使用difference list解决第三个问题
difference list是Prolog的语言特性,Python不支持