资讯专栏INFORMATION COLUMN

【LeetCode 二叉树专项】寻找重复的子树(652)

leejan97 / 2445人阅读

摘要:文章目录题目示例说明限制解法一分析实现复杂度题目给定一棵二叉树的根节点,请返回所有的重复子树。示例示例输入输出示例输入输出示例输入输出说明来源力扣链接限制二叉树中的节点数量在之间。

1. 题目

给定一棵二叉树的根节点,请返回所有的重复子树。对于每一类重复子树,你只需要返回其中任意一个的根节点即可。如果两棵二叉树拥有相同的结构和节点值,则称这两棵二叉树是重复的。

1.1 示例

  • 示例 1 1 1

    • 输入: root = [1, 2, 3, 4, null, 2, 4, null, null, 4]
    • 输出: [[2, 4], [4]]
  • 示例 2 2 2

    • 输入: root = [2, 1, 1]
    • 输出: [[1]]

  • 示例 3 3 3

    • 输入: root = [2, 2, 2, 3, null, 3, null]
    • 输出: [[2, 3], [3]]

1.2 说明

1.3 限制

  • 二叉树中的节点数量在 [ 1 ,   1 0 4 ] [1,/textit{ }10^4] [1, 104] 之间;
  • -200 <= Node.val <= 200

2. 解法一

2.1 分析

想要正确解答本题,只需要知道如何给出二叉树的某种唯一性表达即可,接下来首先获得以给定二叉树的所有节点为根节点的子二叉树的表达形式,然后得到所有重复表达形式对应子树的根节点即可。

实际上,所谓给出二叉树的某种唯一表达其实就是对二叉树进行序列化,而在【LeetCode 二叉树专项】二叉树的序列化与反序列化(297)中,我们给出了基于二叉树深度优先搜索(前序遍历、后序遍历)以及广度优先搜索实现的二叉树序列化。

接下来,需要确定就是具体采用哪种方式序列化。本题采用二叉树的后序遍历对给定二叉树的所有子树进行序列化

2.2 实现

from json import dumpsfrom typing import Optional, List, Set, Dict, Unionclass TreeNode:    def __init__(self, val=0, left: "TreeNode" = None, right: "TreeNode" = None):        self.val = val        self.left = left        self.right = rightclass Solution:    def _find_duplicate_subtrees(self, root: TreeNode,                                 memorandum: Dict[str, int],                                 results: List[TreeNode]) -> Optional[List[int]]:        if not isinstance(root, TreeNode):            return        left = self._find_duplicate_subtrees(root.left, memorandum, results)        right = self._find_duplicate_subtrees(root.right, memorandum, results)        postorder = []        if left:            postorder = postorder + left        else:            postorder.append(left)        if right:            postorder = postorder + right        else:            postorder.append(right)        postorder.append(root.val)        dumped = dumps(postorder)        # if dumped in memorandum.keys() and memorandum[dumped] == 1:        if memorandum.get(dumped, 0) == 1:  # The above commented if-clause is a pedantic counterpart.            results.append(root)        memorandum[dumped] = memorandum.get(dumped, 0) + 1        return postorder    def find_duplicate_subtrees(self, root: Optional[TreeNode]) -> Optional[List[TreeNode]]:        if not isinstance(root, TreeNode):            return        memorandum = dict()        results = []        self._find_duplicate_subtrees(root, memorandum, results)        print("memorandum =", memorandum)        return resultsdef postorder(root: TreeNode, tree: List[Union[int, None]]):    if not root:        tree.append(None)        return    postorder(root.left, tree)    postorder(root.right, tree)    tree.append(root.val)def main():    node7 = TreeNode(4)    node6 = TreeNode(4)    node5 = TreeNode(2, left=node7)    node4 = TreeNode(4)    node3 = TreeNode(3, node5, node6)    node2 = TreeNode(2, left=node4)    node1 = TreeNode(1, node2, node3)    root = node1    sln = Solution()    for root in sln.find_duplicate_subtrees(root):        tree = []        postorder(root, tree)        print("postorder =", dumps(tree))if __name__ == "__main__":    main()

运行上述解答的输出结果为:

memorandum = {"[null, null, 4]": 3, "[null, null, 4, null, 2]": 2, "[null, null, 4, null, 2, null, null, 4, 3]": 1, "[null, null, 4, null, 2, null, null, 4, null, 2, null, null, 4, 3, 1]": 1}postorder = [null, null, 4]postorder = [null, null, 4, null, 2]

实际上, LeetCode 官方通过使用 collections 模块中的 Counter 类,给出了一个如下的非常优雅的解答,其中关于 Counter 类的使用,可以参考【源码共读】Python 标准模块 collections 中 Counter 类详解

from typing import Optional, List, Dict, Unionfrom collections import Counterclass TreeNode:    def __init__(self, val=0, left: "TreeNode" = None, right: "TreeNode" = None):        self.val = val        self.left = left        self.right = rightclass Solution:    def _collect(self, node: TreeNode, duplicates: List[TreeNode], count: Counter):        if not node:            return "#"        serial = "{},{},{}".format(self._collect(node.left, duplicates, count),                                   self._collect(node.right, duplicates, count),                                   node.val)        count[serial] += 1        if count[serial] == 2:            duplicates.append(node)        return serial    def elegantly_find_duplicate_subtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]:        count = Counter()        duplicates = []        self._collect(root, duplicates, count)        return duplicatesdef postorder(root: TreeNode, tree: List[Union[int, None]]):    if not root:        tree.append(None)        return    postorder(root.left, tree)    postorder(root.right, tree)    tree.append(root.val)def main():    node7 = TreeNode(4)    node6 = TreeNode(4)    node5 = TreeNode(2, left=node7)    node4 = TreeNode(4)    node3 = TreeNode(3, node5, node6)    node2 = TreeNode(2, left=node4)    node1 = TreeNode(1, node2, node3)    root = node1    sln = Solution()    for root in sln.elegantly_find_duplicate_subtrees(root):        tree = []        postorder(root, tree)        print("postorder =", dumps(tree))if __name__ == "__main__":    main()

实际上,在上述解答的 第 15 15 15 行中,如果将 return "#" 改为 return 也是正确的,但不为何此时在 LeetCode 上提交时,执行耗时和内存消耗的数据都非常差,希望看到这篇题解而且知道缘由的有缘人可以在评论区留下你的指导。

2.3 复杂度

  • 时间复杂度: O ( N 2 ) O(N^2) O(N2) ,当给定二叉树的所有节点呈一条链状时,比较容易分析其最坏时间复杂度: 1 + 2 + ⋯ + N 1+2+/cdots+N 1+2++N
  • 空间复杂度: O ( N 2 ) O(N^2) O(N2) ,当给定二叉树的所有节点呈一条链状时,比较容易分析其最坏空间复杂度: 1 + 2 + ⋯ + N 1+2+/cdots+N 1+2++N

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/123340.html

相关文章

  • 力扣(LeetCode)652

    摘要:对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。两棵树重复是指它们具有相同的结构以及相同的结点值。这里再用存储,键序列化结果,值树节点组成的链表。 题目地址:https://leetcode-cn.com/probl...题目描述:给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。 两棵树重复是指它们具有相同的结构以及相同的结点...

    Noodles 评论0 收藏0
  • LeetCode 叉树专项】把二叉搜索树转换为累加树(538)

    摘要:解法一中序遍历分析由于给定了二叉搜索树,因此首先考虑中序遍历,使用示例,我们先来分别看一下二叉搜索树和累加树中序遍历的结果二叉搜索树二叉累加树。这里还是使用示例,我们再来观察一下二叉搜索树和累加树中序遍历的结果二叉搜索树二叉累加树。 ...

    xcold 评论0 收藏0
  • ⭐算法入门⭐《叉树 - 二叉搜索树》简单05 —— LeetCode 897. 递增顺序搜索树

    文章目录 一、题目1、题目描述2、基础框架3、原题链接 二、解题报告1、思路分析2、时间复杂度3、代码详解 三、本题小知识四、加群须知 一、题目 1、题目描述   给你一棵二叉搜索树,请按 中序遍历 将其重新排列为一棵递增顺序搜索树,使树中最左边的节点成为树的根节点,并且每个节点没有左子节点,只有一个右子节点。  样例输入: [5,3,6,2,4,null,8,1,null,null,nu...

    Soarkey 评论0 收藏0
  • leetcode235-236 lowest common ancestor

    摘要:如果左右子树返回的最低共同父节点值都不是空,说明和分别位于的左右子树,那么就是最低共同父节点。 235题-题目要求 Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST. According to the definition of L...

    chadLi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<