在问 · 算法题解

652. 寻找重复的子树

中等二叉树子树序列化 + 哈希表力扣原题 ↗

题目描述

输入一棵二叉树的根节点 root,用层序数组表示,null 表示空位。

以某个节点为根、连同其全部后代构成一棵子树。如果两棵子树形状相同,并且对应位置的节点值也都相同,就认为它们重复。

找出所有至少出现两次的子树。同一类重复子树只需返回其中任意一棵的根节点,结果列表的顺序不限。输出中每棵子树都以它自己的层序数组表示。

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

解释:子树 [4] 出现了三次,子树 [2,4] 出现了两次。

示例 2
输入:root = [2,1,1]
输出:[[1]]

解释:两个叶子 1 构成的子树相同。

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

解释:子树 [3] 和 [2,3] 各出现两次。

数据范围

  • 节点数在 [1, 5000] 范围内。
  • -200 <= Node.val <= 200

解题思路:子树序列化 + 哈希表

把每棵子树序列化成一个唯一的字符串:后序遍历,先拿到左右子树的字符串,再拼成“值,左,右”,空节点用 # 表示。结构和值都相同 ⇔ 字符串相同。用哈希表记录每个字符串出现的次数,恰好第 2 次出现时把当前节点加入答案,这样每类只加一次。

为什么正确

ser(node) 返回的字符串唯一确定以 node 为根的子树;处理完 node 时,它的全部后代都已计入哈希表。

复杂度分析

时间 O(n²):每个节点拼接的字符串长度可达 O(n)(用整数编号代替字符串可优化到 O(n));空间 O(n²)。

易错点

序列化必须包含空节点标记和分隔符:只拼值会把 [1,2] 与 [1,null,2]、或 "1"+"12" 与 "11"+"2" 混淆。另外要用“次数恰好等于 2”而不是“大于等于 2”,否则同一类会被加入多次。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]:
        seen = {}
        ans = []
        def ser(node):
            if not node:
                return '#'
            key = str(node.val) + ',' + ser(node.left) + ',' + ser(node.right)
            seen[key] = seen.get(key, 0) + 1
            if seen[key] == 2:
                ans.append(node)
            return '(' + key + ')'
        ser(root)
        return ans

载入编辑器,自己跑一遍 →

其他解法

两种解法都用后序遍历给子树生成唯一标识;序列化字符串长度随子树增长,最坏 O(n²),三元组编号把标识压缩成整数,降到 O(n)。

解法 2:三元组编号 时间 O(n),空间 O(n)

给每种不同的子树结构分配一个整数编号,空树编号为 0。后序遍历时,先得到左右子树的编号,再用三元组 (val, 左编号, 右编号) 在哈希表中查找。三元组已存在就复用其编号,否则分配新编号。某个编号第二次出现时,把当前节点加入答案。

每个节点的键长度是常数,避免了序列化字符串随子树增长。代价是需要理解“子树相同当且仅当三元组相同”这一归纳关系。

class Solution:
    def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]:
        ids = {}
        count = {}
        ans = []

        def dfs(node):
            if not node:
                return 0
            key = (node.val, dfs(node.left), dfs(node.right))
            if key not in ids:
                ids[key] = len(ids) + 1
            uid = ids[key]
            count[uid] = count.get(uid, 0) + 1
            if count[uid] == 2:
                ans.append(node)
            return uid

        dfs(root)
        return ans

二叉树 专题的其他题目

查看 二叉树 全部题目 →