剑指 Offer 07. 重建二叉树


题目:剑指 Offer 07. 重建二叉树

解题思路1:

首先考虑前序遍历的特点,第一个节点是根节点;而后考虑中序遍历的结果,根节点左侧出现的均为左子树上的值,右侧的均为右子树上的值,然后由此在前序遍历中找到分界点进行递归,最终构建出整个树。(每次数组切片产生的开销过大。。强行增大了时间和空间)
时间:\(O(N)\)
空间:\(O(N^2)\)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if not preorder:
            return None
        root = TreeNode(preorder[0])
        r_pos = inorder.index(preorder[0])
        root.left = self.buildTree(preorder[1:r_pos + 1], inorder[0:r_pos])
        root.right = self.buildTree(preorder[r_pos + 1:], inorder[r_pos + 1:])
        return root
优质解答1:递归(参考自K神)

新构建一个递归函数,只传入索引即可,并建立字典存储中序遍历中的索引以方便后续切分左右子树,其他与上述题解思路一样。(虽然思路一样,这代码的速度和内存的消耗差的太多了。。只能说K神yyds)
时间:\(O(N)\)
空间:\(O(N)\)

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        def recur(root, left, right):
            if left > right: return                               # 递归终止
            node = TreeNode(preorder[root])                       # 建立根节点
            i = dic[preorder[root]]                               # 划分根节点、左子树、右子树
            node.left = recur(root + 1, left, i - 1)              # 开启左子树递归
            node.right = recur(i - left + root + 1, i + 1, right) # 开启右子树递归
            return node                                           # 回溯返回根节点

        dic, preorder = {}, preorder
        for i in range(len(inorder)):
            dic[inorder[i]] = i
        return recur(0, 0, len(inorder) - 1)
优质解答2:迭代(参考自官方)

申请一个栈用于保存暂时未找到右孩子的节点,我们建一个指针ind_pre遍历前序的序列,用ind_in遍历中序序列,初始ind_pre=1,ind_in=0。因为根据前序遍历(根左右),以及中序遍历的序列中最开始的一定是树最左端的节点,如果此时栈顶节点不等于ind_in指向的节点,则代表ind_pre指向节点必为栈顶节点的左孩子,并将其入栈,ind_pre后移;如果此时栈顶元素等于ind_in指向的节点,则代表ind_pre指向节点必为栈内某一节点的右孩子,而栈中存储的节点顺序与它们在中序遍历中出现的顺序是相反的,所以以此和中序遍历进行对照,如果从一个节点处开始不满足逆序关系,则其在前序遍历中的前一个节点就为ind_pre的父节点。如果栈非空且栈顶节点与ind_in指针指向的元素相同(如果栈空了则默认最后一个出栈元素为其父节点),则弹栈,ind_in指针后移,继续比对,等找到一个栈顶节点与ind_in指向的节点不同时,我们知道前一个栈顶元素就是此时ind_pre指向节点的父节点,并且ind_pre是其右孩子,因为该节点也暂时未找到右孩子,所以将其入栈。如此往复,直到遍历结束。
时间:\(O(N)\)
空间:\(O(N)\)

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if not preorder:
            return
        stack = []
        root = TreeNode(preorder[0])
        stack.append(root)
        ind_in = 0
        for ind_pre in range(1, len(preorder)) :
            node = stack[-1]
            if inorder[ind_in] != stack[-1].val:
                new_node = TreeNode(preorder[ind_pre])
                stack[-1].left = new_node
                stack.append(new_node)
            else:
                while stack and stack[-1].val == inorder[ind_in]:
                    node = stack.pop()
                    ind_in += 1
                node.right = TreeNode(preorder[ind_pre])
                stack.append(node.right)
        return root

总体来说还是推荐递归吧,更好理解,代码也更简洁