二叉树:已知前序序列与后序序列建树

本文探讨了如何根据二叉树的前序和后序序列来唯一确定树的结构。通过一系列分析,揭示了在特定条件下树不唯一的原因,并提供了一个确定树是否唯一的特征。文章还提供了若干练习案例,帮助读者掌握这一算法。

二叉树:已知前序与后序建树

已知前序与中序、后序与中序建树是常遇到的算法问题。若已知前序序列与后序序列,要求输出满足条件的树的个数或者输出所有可能的树的中序序列,该怎么解决?下面我们一步步讨论这个问题。

首先,已知一个树的结点数n,共有多少种树形?(或者,已知一颗树的先序/中序/后序序列,共有多少种树形?)
答案是 C m n / ( n + 1 ) C_{m}^{n}/(n+1) Cmn/(n+1)种。点这里了解一下卡特兰数
那么,这里就有一种暴力解法,就是建立所有可能的树形,将先序序列填入某一树形,比较此树的后序序列是否与给定后序序列相同,若相同则找到一个解,若不相同,则填入下一个树形,直到填完所有可能树形。当树的结点为10时,那么总共有16796种树形,因此这种暴力解法时间复杂度相当高
那么我们换一种思考方式,我们先来看看先序与后序序列的排布规律。以下面这棵树来举例:

在这里插入图片描述

其先序序列为: 1 2 3 4 6 7 5
后序序列为:2 6 7 4 5 3 1

首先我们要知道:

先序序列遍历顺序是:根结点-左子树-右子树

后序序列遍历顺序是:左子树-右子树-根结点

很明显,我们可以看出结点在先、后序列中的排布有以下这些特征
【1】、在先序序列中,根结点在子树中的结点前面,在后序序列中,根结点在子树中的结点后面
【2】、以任一节点为根结点时,其子树在后序序列中排布都是先左子树后右子树而根结点排在最后
那么,反过来思考,已知这个先序与后序序列所确定的树是唯一的吗?进一步推广:怎么通过先序与后序序列判断是否存在唯一的树呢?

现在,我们来一步步分析已知先序与后序的建树过程:

①、根据特征【1】可知:根结点为先序序列第一个节点以及后序序列最后一个结点,因此根结点为1
在这里插入图片描述
②、先序序列中第二个结点为2,其在后序序列中的位置是第一个,那么根据特征【2】我们可以知道结点2是没有子树的,而且结点2要么在根结点的左子树,要么在右子树。假设结点2在右子树,那么由特征【2】可知根结点1没有左子树,而且先序序列中结点2后面的结点全部为结点2的子树上的结点。再看后序序列,由特征【2】可知,结点2后面的结点不可能是其子树上的结点。因此,假设显然与已知矛盾。这样,我们又知道结点2是结点1的左孩子,且结点2没有子结点
在这里插入图片描述
③、先序序列第三个位置上的结点为3,该结点在后序序列中排倒数第二个。由②可知,结点3必然是根结点1的右孩子。
在这里插入图片描述
④、先序序列第四个位置上的结点为4,该结点在后序序列中排第四个。因为结点4在先序序列中排在结点3后面,又因为结点3是根结点1的右孩子,所以结点4只可能在结点3的子树上。结点3的子树可能出现的情况是:只有左子树,只有右子树,左右子树都有。因为在后序序列中,结点4左边是结点6、7,右边是结点5。所以结点5并不在结点4的子树上,所以结点3既存在左子树又存在右子树。这样的话,出现在结点3后面的第

要根据完全二叉树后序遍历列重建二叉树,可以通过结合额外的信息来实现这一目标。通常情况下,单靠一种遍历方式无法唯一确定一棵二叉树[^1]。然而,在某些特殊条件下(如完全二叉树),如果能够利用其他性质或者辅助条件,则可能完成此操作。 以下是基于后序遍历列以及完全二叉树特性的一种方法: ### 方法概述 对于完全二叉树而言,其结构具有一定的规律性。假设我们已经知道某棵完全二叉树后序遍历结果 `postorder`,那么可以根据以下逻辑逐步恢复原树: #### 后序遍历的特点 在后序遍历中,最后一个元素总是当前子树的根节点[^4]。因此可以从后向前依次处理这些节点,并按照完全二叉树的定义填充左右孩子节点的位置关系。 #### 完全二叉树的层次编号规则 为了进一步简化问题,我们可以引入数组索引来表示每个结点所在位置的关系。具体来说: - 如果某个父节点位于数组中的下标为 i (从零开始计数),则它的左孩子的下标应为 \(2i+1\); - 右孩子的下标则是 \(2i+2\)[^3]。 借助上述两点观察,下面给出具体的算法描述及其 Python 实现代码。 ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def buildTree(postorder): if not postorder: return None n = len(postorder) root_index_map = {} for idx, value in enumerate(reversed(postorder)): root_index_map[value] = idx max_idx = 2 ** int(math.log(n + 1, 2)) - 2 if n != 0 else -1 nodes = [None]*len(postorder) def construct(index_in_postorder): nonlocal nodes if index_in_postorder >=n or index_in_postorder<0 : return None node_val=postorder[max_idx-index_in_postorder] current_node =TreeNode(node_val) nodes[index_in_postorder]=current_node left_child_pos=(index_in_postorder*2)+1 right_child_pos=(index_in_postorder*2)+2 current_node.left=construct(left_child_pos)if left_child_pos<=max_idx else None current_node.right=construct(right_child_pos)if right_child_pos<=max_idx else None return current_node constructed_root=construct(0) return constructed_root ``` 注意这里采用了递归的方式构建整个树形结构,并且通过调整索引映射实现了从后往前逐层建立父子关联的操作流程。 --- ###
评论 12
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值