剑指Offer,给定前序与中序序列,还原二叉树。

最近在刷剑指offer上面的习题,但是发现好像目前网上用python写的比较详细的教程不多。所以写下这篇文章供大家参考,特别适用于新手。如有好的提议,欢迎提出。对了,转载请注明作者出处,我叫米行老板,谢谢。

在这里插入图片描述

# 题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历
和中序遍历的结果中都不含重复的数字。
# 例如:前序遍历序列{1, 2, 4, 7, 3, 5, 6, 8}和中序遍历序列{4, 7, 2, 1, 5, 3, 8, 6},
# 重建出上图所示的二叉树并输出它的头结点。

class Node:
    def __init__(self, value):
        self.value = value  # 当前节点的值
        self.left = None  # 左子节点
        self.right = None  # 右子节点


class Solution:

    def rebuild(self, pre_, in_):
        """
        重建二叉树
        :param pre_: 前序遍历序列
        :param in_: 中序遍历序列
        :return: 建好后的二叉树的头节点
        """

        # 如果递归到最后,数组为空,则说明该子树为空
        if len(pre_) == 0:
            return None
        
        # 如果递归到最后,数组中只剩一个元素,则返回该元素
        elif len(pre_) == 1:
            return Node(pre_[0])
        
        # 如果还未迭代到只剩一个元素或空数组,则进行递归
        else:
            # 构建目标二叉树的根节点(前序遍历次序:中,左,右)
            cur = Node(pre_[0])
            
            # 获取前序序列中的第一个元素值,该值为目标二叉树的根节点的值
            root = pre_[0]
            
            # 从中序序列中获取目标二叉树的根节点的索引,作为二叉树的分割点
            pivot = in_.index(root)

            # [1, 2, 4, 7, 3, 5, 6, 8]
            #  ↑
            # 根节点的值

            # [4, 7, 2, 1, 5, 3, 8, 6]
            #           ↑
            #       根节点的值
            # 左边为左子树的值,右边为右子树的值

            # 如上方示例所示
            # 从第【二】个元素到根节点的下标位置,为【前序序列】的【左子树】部分
            pre_left = pre_[1:pivot + 1]
            
            # 从第【一】个元素到根节点的下标位置,为【中序序列】的【左子树】部分
            in_left = in_[:pivot]
            
            # 左子树开始递归,递归返回值记录为【左子节点】
            cur.left = self.rebuild(pre_left, in_left)

            # 从【根节点】的下标位置到数组结尾,为【前序序列】的【右子树】部分
            pre_right = pre_[pivot + 1:]
            
            # 从根节点的下标位置到数组结尾,为【中序序列】的【右子树】部分
            in_right = in_[pivot + 1:]
            
            # 右子树开始递归,递归返回值记录为【右子节点】
            cur.right = self.rebuild(pre_right, in_right)

            return cur

    def traverse(self, root):
        """
        层序遍历
        :param root: 二叉树的根节点
        :return:
        """
        
        # 将根节点存入队列
        queue = [root]
        
        # 如果根节点中仍有元素
        while queue:
            
            # 取出队列中第一个元组
            cur = queue.pop(0)
            
            # 打印当前值
            print(cur.value, end=' ')
            
            # 如果存在左子树,则将左子树加入队列
            if cur.left:
                queue.append(cur.left)
                
            # 如果存在右子树,则将右子树加入队列
            if cur.right:
                queue.append(cur.right)

    def pre_order(self, root):
        """
        先序遍历(打印顺序:根节点 → 左子树 → 右子树)
        :param root: 二叉树的根节点
        :return:
        """
        
        # 如果根节点不为空
        if root:
            
            # 先打印根节点的值
            print(root.value, end=' ')
            
            # 如果左子节点存在,则左子节点进入递归,成为下一级的根节点
            if root.left:
                self.pre_order(root.left)
                
            # 如果右子节点存在,则右子节点进入递归,成为下一级的根节点
            if root.right:
                self.pre_order(root.right)

    def in_order(self, root):
        """
        中序遍历(打印顺序:左子树 → 根节点 → 右子树)
        :param root: 二叉树的根节点
        :return:
        """
        
        # 如果根节点不为空
        if root:
            
            # 如果左子节点存在,则左子节点进入递归,成为下一级的根节点
            if root.left:
                self.in_order(root.left)
                
            # 当前节点的值,作为根节点被打印
            print(root.value, end=' ')
            
            # 如果右子节点存在,则右子节点进入递归,成为下一级的根节点
            if root.right:
                self.in_order(root.right)

    def post_order(self, root):
        """
        后序遍历(打印顺序:左子树 → 右子树 → 根节点)
        :param root: 二叉树的根节点
        :return:
        """
        
        # 如果根节点不为空
        if root:
            
            # 如果左子节点存在,则左子节点进入递归,成为下一级的根节点
            if root.left:
                self.post_order(root.left)
                
            # 如果右子节点存在,则右子节点进入递归,成为下一级的根节点
            if root.right:
                self.post_order(root.right)
                
            # 当前节点的值,作为根节点被打印
            print(root.value, end=' ')


def examine(solution, result):
    """
    检验函数
    :param solution: 测试类对象
    :param result: 被测试的二叉树
    :return:
    """
    
    # 层序遍历
    print("Traverse:")
    solution.traverse(result)
    print()
    
    # 前序遍历
    print("Pre-order: ")
    solution.pre_order(result)
    print()
    
    # 中序遍历
    print("In-order: ")
    solution.in_order(result)
    print()
    
    # 后序遍历
    print("Post-order: ")
    solution.post_order(result)


def main():
    s = Solution()
    
    # 前序序列
    pre_list = [1, 2, 4, 7, 3, 5, 6, 8]
    
    # 中序序列
    in_list = [4, 7, 2, 1, 5, 3, 8, 6]
    
    # 基于前序与中序序列重构二叉树
    result = s.rebuild(pre_list, in_list)
    
    # 检验结果
    examine(s, result)


if __name__ == "__main__":
    main()

输出结果:
traverse:
1 2 3 4 5 6 7 8
pre-order:
1 2 4 7 3 5 6 8
in-order:
4 7 2 1 5 3 8 6
post-order:
7 4 2 5 8 6 3 1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值