算法数据结构基础——二叉树的还原(基于遍历序列还原二叉树)

1. 二叉树的还原简介

二叉树的还原:指的是通过二叉树的遍历序列,还原出对应的二叉树。

从二叉树的遍历过程可以看出,给定一棵非空二叉树,它的前序、中序、后续遍历所得到的遍历序列都是唯一的。那么反过来,如果已知节点的某种遍历序列,能否确定这棵二叉树呢?并且确定的二叉树是否是唯一的呢?

我们先来回顾一下二叉树的前序遍历、中序遍历、后序遍历规则。

  • 非空二叉树的前序遍历规则:

    1. 访问根节点。

    2. 以前序遍历的方式遍历根节点的左子树。

    3. 以前序遍历的方式遍历根节点的右子树。

  • 非空二叉树的中序遍历规则:

    1. 以中序遍历的方式遍历根节点的左子树。

    2. 访问根节点。

    3. 以中序遍历的方式遍历根节点的右子树。

  • 非空二叉树的后序遍历规则:

    1. 以后序遍历的方式遍历根节点的左子树。

    2. 以后序遍历的方式遍历根节点的右子树。

    3. 访问根节点。

先来看二叉树的前序遍历,前序遍历过程中首先访问的是根节点,所以通过前序遍历序列,我们可以确定序列的第 $1$ 个节点肯定是根节点。但是从第 $2$ 个节点开始就不确定它是根节点的左子树还是根节点的右子树了。所以单凭前序遍历序列是无法恢复一棵二叉树的。

再来看二叉树的后序遍历,后序遍历也是只能确定序列的最后一个节点为根节点,而无法确定其他节点在二叉树中的位置。所以单凭后序遍历序列也是无法恢复一棵二叉树的。

最后我们来看二叉树的中序遍历,中序遍历是先遍历根节点的左子树,然后访问根节点,最后遍历根节点的右子树。这样,根节点在中序遍历序列中必然将中序序列分割成前后两个子序列,其中前一个子序列是根节点的左子树的中序遍历序列,后一个子序列是根节点的右子树的中序遍历序列。当然单凭中序遍历序列也是无法恢复一棵二叉树的。

但是如果我们可以将「前序遍历序列」和「中序遍历序列」相结合,那么我们就可以通过上面中序遍历序列中的两个子序列,在前序遍历序列中找到对应的左子序列和右子序列。在前序遍历序列中,左子序列的第 1 个节点是左子树的根节点,右子序列的第 1 个节点是右子树的根节点。这样,就确定了二叉树的 3 个节点。

同时,左子树和右子树的根节点在中序遍历序列中又可以将左子序列和右子序列分别划分成两个子序列。如此递归下去,当确定了前序遍历序列中的所有节点时,我们就得到了一棵二叉树。

还有一个问题,通过前序序列和中序序列还原的二叉树是唯一的吗?

这个唯一性可以利用归纳法加以证明。感兴趣的读者可以试试自己证明或者参考有关资料。

通过上述过程说明:如果已知一棵二叉树的前序序列和中序序列,可以唯一地确定这棵二叉树。

同理,如果已知一棵二叉树的中序序列和后序序列,也可以唯一地确定这棵二叉树。 方法和通过二叉树的前序序列和中序序列构造二叉树类似,唯一不同点在于二叉树的根节点是根据后序遍历序列的最后一个元素确定的。

类似的,已知二叉树的「中序遍历序列」和「层序遍历序列」,也可以唯一地确定一棵二叉树。

需要注意的是:如果已知二叉树的「前序遍历序列」和「后序遍历序列」,是不能唯一地确定一棵二叉树的。 这是因为没有中序遍历序列无法确定左右部分,也就无法进行子序列的分割。

只有二叉树中每个节点度为 $2$ 或者 $0$ 的时候,已知前序遍历序列和后序遍历序列,才能唯一地确定一颗二叉树,如果二叉树中存在度为 $1$ 的节点时是无法唯一地确定一棵二叉树的,这是因为我们无法判断该节点是左子树还是右子树。

2. 从前序与中序遍历序列构造二叉树

  • 描述:已知一棵二叉树的前序遍历序列和中序遍历序列。

  • 要求:构造出该二叉树。

  • 注意:假设树中没有重复的元素。

2.1 从前序与中序遍历序列构造二叉树实现过程

前序遍历的顺序是:根节点 - 左子树 - 右子树。中序遍历的顺序是:左子树 - 根节点 - 右子树。

根据前序遍历的顺序,可以找到根节点位置。然后在中序遍历的结果中可以找到对应的根节点位置,就可以从根节点位置将二叉树分割成左子树、右子树。同时能得到左右子树的节点个数。

此时构建当前节点,并递归建立左右子树,在左右子树对应位置继续递归遍历进行上述步骤,直到节点为空,具体操作步骤如下:

  1. 从前序遍历顺序中得到当前根节点的位置在 postorder[0]

  2. 通过在中序遍历中查找上一步根节点对应的位置 inorder[k],从而将二叉树的左右子树分隔开,并得到左右子树节点的个数。

  3. 从上一步得到的左右子树个数将前序遍历结果中的左右子树分开。

  4. 构建当前节点,并递归建立左右子树,在左右子树对应位置继续递归遍历并执行上述三步,直到节点为空。

2.2 从前序与中序遍历序列构造二叉树实现代码

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        def createTree(preorder, inorder, n):
            if n == 0:
                return None
            k = 0
            while preorder[0] != inorder[k]:
                k += 1
            node = TreeNode(inorder[k])
            node.left = createTree(preorder[1: k + 1], inorder[0: k], k)
            node.right = createTree(preorder[k + 1:], inorder[k + 1:], n - k - 1)
            return node
        return createTree(preorder, inorder, len(inorder))

3. 从中序与后序遍历序列构造二叉树

  • 描述:已知一棵二叉树的中序遍历序列和后序遍历序列。

  • 要求:构造出该二叉树。

  • 注意:假设树中没有重复的元素。

3.1 从中序与后序遍历序列构造二叉树实现过程

中序遍历的顺序是:左子树 - 根节点 - 右子树。后序遍历的顺序是:左子树 - 右子树 - 根节点。

根据后序遍历的顺序,可以找到根节点位置。然后在中序遍历的结果中可以找到对应的根节点位置,就可以从根节点位置将二叉树分割成左子树、右子树。同时能得到左右子树的节点个数。

此时构建当前节点,并递归建立左右子树,在左右子树对应位置继续递归遍历进行上述步骤,直到节点为空,具体操作步骤如下:

  1. 从后序遍历顺序中当前根节点的位置在 postorder[n-1]

  2. 通过在中序遍历中查找上一步根节点对应的位置 inorder[k],从而将二叉树的左右子树分隔开,并得到左右子树节点的个数。

  3. 从上一步得到的左右子树个数将后序遍历结果中的左右子树分开。

  4. 构建当前节点,并递归建立左右子树,在左右子树对应位置继续递归遍历并执行上述三步,直到节点为空。

3.2 从中序与后序遍历序列构造二叉树实现代码

class Solution:
    def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
        def createTree(inorder, postorder, n):
            if n == 0:
                return None
            k = 0
            while postorder[n - 1] != inorder[k]:
                k += 1
            node = TreeNode(inorder[k])
            node.right = createTree(inorder[k + 1: n], postorder[k: n - 1], n - k - 1)
            node.left = createTree(inorder[0: k], postorder[0: k], k)
            return node
        return createTree(inorder, postorder, len(postorder))

4. 从前序与后序遍历序列构造二叉树

前边我们说过:已知二叉树的前序遍历序列和后序遍历序列,是不能唯一地确定一棵二叉树的。 而如果不要求构造的二叉树是唯一的,只要求构造出一棵二叉树,还是可以进行构造的。

  • 描述:已知一棵二叉树的前序遍历序列和后序遍历序列。

  • 要求:重构并返回该二叉树。

  • 注意:假设树中没有重复的元素。如果存在多个答案,则可以返回其中任意一个。

4.1 从前序与后序遍历序列构造二叉树实现过程

我们可以默认指定前序遍历序列的第 2 个值为左子树的根节点,由此递归划分左右子序列。具体操作步骤如下:

  1. 从前序遍历序列中可知当前根节点的位置在 preorder[0]

  2. 前序遍历序列的第 2 个值为左子树的根节点,即 preorder[1]。通过在后序遍历中查找上一步根节点对应的位置 postorder[k](该节点右侧为右子树序列),从而将二叉树的左右子树分隔开,并得到左右子树节点的个数。

  3. 从上一步得到的左右子树个数将后序遍历结果中的左右子树分开。

  4. 构建当前节点,并递归建立左右子树,在左右子树对应位置继续递归遍历并执行上述三步,直到节点为空。

4.2 从前序与后序遍历序列构造二叉树实现代码

class Solution:
    def constructFromPrePost(self, preorder: List[int], postorder: List[int]) -> TreeNode:
        def createTree(preorder, postorder, n):
            if n == 0:
                return None
            node = TreeNode(preorder[0])
            if n == 1:
                return node
            k = 0
            while postorder[k] != preorder[1]:
                k += 1
            node.left = createTree(preorder[1: k + 2], postorder[: k + 1], k + 1)
            node.right = createTree(preorder[k + 2: ], postorder[k + 1: -1], n - k - 2)
            return node
        return createTree(preorder, postorder, len(preorder))
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 已知二叉树的后序遍历序列,可以通过递归的方式还原二叉树。 具体步骤如下: 1. 后序遍历序列的最后一个元素为二叉树的根节点。 2. 从后序遍历序列的第一个元素开始,找到第一个大于根节点的元素,它之前的所有元素都属于根节点的左子树,之后的所有元素都属于根节点的右子树。 3. 递归处理左子树和右子树,直到序列为空或只有一个元素。 举个例子,假设后序遍历序列为[4, 5, 2, 6, 3, 1],那么1就是根节点,[4, 5, 2, 6, 3]就是左子树和右子树的后序遍历序列。 接下来,我们可以找到第一个大于1的元素为3,所以[4, 5, 2, 6]是左子树的后序遍历序列,[3]是右子树的后序遍历序列。 继续递归处理左子树和右子树,左子树的根节点为3,左子树为空,右子树的根节点为6,左子树为空,右子树为2,左子树为4,右子树为5。 最终还原出的二叉树如下所示: ``` 1 / \ 3 2 / \ 6 5 / 4 ``` ### 回答2: 已知二叉树的后序遍历序列如何还原。 后序遍历二叉树遍历的一种方式,遍历顺序是先遍历左子树,再遍历右子树,最后访问根节点。根据后序遍历序列可以还原出原始的二叉树的结构。方法如下: 1. 首先,找到二叉树的根节点。后序遍历序列的最后一个元素即为根节点。 2. 在后序遍历序列中找到根节点后,可以将后序遍历序列分为两部分:左子树的后序遍历序列和右子树的后序遍历序列。 3. 对于左子树的后序遍历序列和右子树的后序遍历序列,可以通过递归的方式继续还原出左子树和右子树。 4. 递归的终止条件是后序遍历序列为空或只有一个元素,此时可以直接返回该节点。 5. 通过递归还原出左子树和右子树后,再将根节点与左子树和右子树连接起来,构成完整的二叉树。 以上步骤可以通过递归算法来实现。我们可以定义一个函数,接收后序遍历序列作为输入,返回还原后的二叉树的根节点。 总结起来,还原二叉树的后序遍历序列的步骤是:找到根节点,分隔出左右子树的后序遍历序列,递归还原左右子树,最后连接根节点和左右子树。 ### 回答3: 已知二叉树的后序遍历序列如何还原,可以通过递归方法进行。后序遍历的顺序是:左子树,右子树,根节点。所以我们可以从后序遍历序列中找到根节点,然后再划分左子树和右子树的序列。 具体步骤如下: 1. 首先,找到根节点。根据后序遍历的顺序,最后一个元素即为根节点的值。 2. 划分左子树和右子树的序列。在后序遍历中,根节点之前的序列中,是左子树的元素;根节点之后的序列中,之前的元素是右子树的元素。 3. 对于左子树和右子树的序列,分别递归地调用还原二叉树的方法,得到左子树和右子树。 4. 将左子树和右子树连接到根节点上,形成一棵完整的二叉树。 5. 最后,返回该二叉树。 需要注意的是,如果给定的后序遍历序列为空,则返回空。 总结起来,还原二叉树的后序遍历序列可以通过递归的方法,先找到根节点,然后根据根节点将序列划分为左子树和右子树,再递归地调用还原方法,最后将左子树和右子树连接到根节点上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值