和二叉树相伴的美好时光~@labuladong Day4 - 构造

写在前面

本篇全部集中在二叉树相关问题上,是参考东哥的思路进行的练习和思考。东哥有《labuladong 的算法小抄》以及宝藏微信公众号 labuladong,github 也有项目,自来水推荐购买和关注。

二叉树思考学习记录

Day4 二叉树的构造

  • 所有的迭代操作都可以改用递归形式来写;
  • 本篇重点围绕使用递归的方式、借助之前遍历二叉树的代码框架,来实现二叉树的增、删、改、造。(Φ皿Φ)吓人~

Day4 练习

我宣布我爱上了递归~ 1.x 这些问题我看了以前的提交都是写的 helper,现在直接在函数签名下面完成函数功能,感觉有点想“装”一下哈哈O(∩_∩)O

1.1 合并两个二叉树

看了以前的写法,思路也还是遍历,但是单独写的 helper。针对这个问题,可以直接使用原题函数签名。

    def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
        # 使用递归的形式,通过遍历的思路来合并两棵树
        # 本函数需要返回合并后的结果,此结果还需要一定要有指针来接收
        if not root1: return root2
        if not root2: return root1
        root1.val += root2.val
        root1.left = self.mergeTrees(root1.left, root2.left)
        root1.right = self.mergeTrees(root1.right, root2.right)
        return root1

1.2 删除指定叶子节点

    def removeLeafNodes(self, root: TreeNode, target: int) -> TreeNode:
        # 借助递归的形式,使用遍历二叉树的思路,来删除指定叶子节点
        # 没路了,原地折回
        if not root: return None
        #递归左右子树,一定要接收返回值
        root.left = self.removeLeafNodes(root.left, target)
        root.right = self.removeLeafNodes(root.right, target)
        # 目标出现,斩掉连接,撤退~
        if not root.left and not root.right and root.val == target:
            return None
        return root

1.3 二叉树剪枝

    def pruneTree(self, root: TreeNode) -> TreeNode:
        # 借助递归的形式,通过遍历二叉树,来完成指定剪枝
        # 到头儿了,往回拐
        if not root: return None
        # 递归左右子树,返回值要有人接
        root.left = self.pruneTree(root.left)
        root.right = self.pruneTree(root.right)
        # 目标出现,切掉,撤退~
        if not root.left and not root.right and root.val == 0:
            return None
        # 返回需要有人接的 root,root 而来,root 而去
        return root

2. 二叉树的序列化与反序列化

前序思路从前往后拿,依次是 root left 和 right;
后序思路从后往前拿,依次是 root right 和 left;
前序后序都可以用递归来做,理论上好像中序也可以类似思路,但是中序思路祖宗 root 藏木于林,我布吉岛它在哪儿,所以无法序列化;

前序:

	# 使用前序遍历的思路进行序列化和反序列化
    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
		# 需要额外定义函数来实现前序遍历序列化,空节点都为 '#'
        def my_serialize(root, res):
            if not root: 
                res.append('#')
                return 
            res.append(str(root.val))
            my_serialize(root.left, res)
            my_serialize(root.right, res)
            return res
        
        res = my_serialize(root, [])
        if not res: return ''
        return ','.join(res)

        

    def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        if not data: return None
        res = collections.deque(data.split(','))
        # 需要额外定义函数来实现中序遍历反序列化
        # 要有返回值,还一定有人接
        def my_deserialize(res):
            if not res: return None
            cur = res.popleft()
            if cur == '#': return None
            root = TreeNode(int(cur))
            root.left = my_deserialize(res)
            root.right = my_deserialize(res)
            return root
        
        return my_deserialize(res)

中序:

    # 使用中序遍历的思路进行序列化和反序列化
    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        # 需要额外定义函数来实现中序遍历序列化,空节点都为 '#'
        def my_serialize(root, res):
            if not root: 
                res.append('#')
                return 
            my_serialize(root.left, res)
            res.append(str(root.val))
            my_serialize(root.right, res)
            return res
        
        res = my_serialize(root, [])
        if not res: return ''
        return ','.join(res)

    def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        if not data: return None
        res = collections.deque(data.split(','))
        print(res)
        # 需要额外定义函数来实现中序遍历反序列化
        # 要有返回值,还一定有人接
        def my_deserialize(res):
            if not res: return None
            # 谁能告诉我根节点在哪里?没有人。
            # 那算了,溜了溜了,不干了
        #     cur = res.pop() 
        #     if cur == '#': return None
        #     root = TreeNode(int(cur))
        #     # 需要先重建右子树,再重建左子树
        #     root.right = my_deserialize(res)
        #     root.left = my_deserialize(res)
        #     return root
        
        # return my_deserialize(res)
        

后序:


	# 使用后序遍历的思路进行序列化和反序列化
    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        # 需要额外定义函数来实现后序遍历序列化,空节点都为 '#'
        def my_serialize(root, res):
            if not root: 
                res.append('#')
                return 
            my_serialize(root.left, res)
            my_serialize(root.right, res)
            res.append(str(root.val))
            return res
        
        res = my_serialize(root, [])
        if not res: return ''
        return ','.join(res)

    def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        if not data: return None
        res = collections.deque(data.split(','))
        # 需要额外定义函数来实现后序遍历反序列化
        # 要有返回值,还一定有人接
        def my_deserialize(res):
            if not res: return None
            cur = res.pop()
            if cur == '#': return None
            root = TreeNode(int(cur))
            root.right = my_deserialize(res)
            root.left = my_deserialize(res)
            return root
        
        return my_deserialize(res)

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

关于计算左子树尺寸,你愿意计算右子树尺寸来做也完全可以。这个维护边界的样子和排序算法如出一辙。

    def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
        # 借助递归的形式,使用遍历二叉树的思路(反过来),构造二叉树
        # 因为过程中 preorder 和 inorder 完全没有变
        # 只是在不断维护左右子树在 preorder 和 inorder 中的区间,所以需要重新写个函数
        def my_build_tree(pre_start, pre_end, in_start, in_end):
            # 树为空啊,没有内容
            if pre_start > pre_end: return None
            # 根节点拿出来
            root = TreeNode(preorder[pre_start])
            # 借助中序遍历计算左子树的 size
            idx = inorder.index(root.val)
            left_tree_size = idx - in_start
            # 根节点下一个就是左子树的左边界,根据左子树 size 推出来右边界
            root.left = my_build_tree(pre_start + 1, pre_start + left_tree_size, in_start, idx - 1)
            # 左子树右边界下个位置就是右子树的左边界,它俩挨着坐的嘛
            root.right = my_build_tree(pre_start + left_tree_size + 1, pre_end, idx + 1, in_end)
            return root
        
        n = len(preorder)
        return my_build_tree(0, n - 1, 0, n - 1)

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

    def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
        # 借助递归的形式,使用遍历二叉树的思路(反过来),构造二叉树
        # 因为过程中 postorder 和 inorder 完全没有变
        # 只是在不断维护左右子树在 postorder 和 inorder 中的区间,所以需要重新写个函数
        def my_build_tree(post_start, post_end, in_start, in_end):
            # 树为空啊,没有内容
            if post_start > post_end: return None
            # 根节点拿出来
            root = TreeNode(postorder[post_end])
            # 借助中序遍历计算左子树的 size
            idx = inorder.index(root.val)
            left_tree_size = idx - in_start
            # 左子树左边界还是在最前面不变,根据左子树 size 推出来右边界
            root.left = my_build_tree(post_start, post_start + left_tree_size - 1, in_start, idx - 1)
            # 左子树右边界下个位置就是右子树的左边界,它俩挨着坐的嘛,然后右边界不能触碰爸爸 root
            root.right = my_build_tree(post_start + left_tree_size, post_end - 1, idx + 1, in_end)
            return root
        
        n = len(postorder)
        return my_build_tree(0, n - 1, 0, n - 1)

Day4 的思考与补充

Leetcode 的层序遍历,一个屏幕放不下,幸好我还有个竖屏~
从老到少按辈分都依次装进麻袋;
另外一头开个口子,老祖宗先出来,然后依次在麻袋口守着等自己孩子;
领完的就走了,当上一个辈分正好都领完,麻袋边上排队的正好是下一个辈分。

    # 使用层序遍历的思路进行序列化和反序列化
    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        # 需要额外定义函数来实现层序遍历序列化,空节点都为 '#'
        if not root: return ''
        res = [str(root.val)]
        que = collections.deque()
        que.append(root)
        
        while que:
            le = len(que)
            temp = []
            has_node = False
            for i in range(le):
                cur = que.popleft()
                if not cur.left: 
                    temp.append('#')
                else:
                    has_node = True
                    temp.append(str(cur.left.val))
                    que.append(cur.left)
                if not cur.right:
                    temp.append('#')
                else:
                    has_node = True
                    temp.append(str(cur.right.val))
                    que.append(cur.right)
            # 全为空节点的最后一层不配进家谱
            if has_node: res.extend(temp)
        return ','.join(res)


    def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        if not data: return None
        res = collections.deque(data.split(','))
        root = TreeNode(int(res.popleft()))

        level_que = collections.deque()
        level_que.append(root)
        # 从老到少都依次在队里了,现在一个个拿出来
        # 之前已出队的非空节点才有孩子可以领
        while res:
            parent = level_que.popleft()
            left = res.popleft()
            if left == '#':
                parent.left = None
            else:
                parent.left = TreeNode(int(left))
                level_que.append(parent.left)
            
            right = res.popleft()
            if right == '#':
                parent.right = None
            else:
                parent.right = TreeNode(int(right))
                level_que.append(parent.right)
        return root


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值