LeetCode】寻找重复子树:深度解析与高效解法
📖 问题描述
给定一棵二叉树的根节点 root
,返回所有重复的子树。若两棵树结构相同且节点值相同,则认为它们是重复的。对于同类重复子树,只需返回其中任意一棵的根节点。
🌰 示例解析
示例1
输入:
1 / \ 2 3 / / \ 4 2 4 / 4
输出:[[2,4],[4]]
解释:
-
子树
[2,4]
出现在根节点左子节点和右子节点的左子节点位置 -
叶子节点
[4]
出现三次
示例2
输入:
2 / \ 1 1
输出:[[1]]
解释:两个叶子节点 [1]
结构相同
🛠️ 解题思路
核心思想:序列化 + 哈希表
-
序列化子树
将每个子树转化为唯一的字符串标识。通过递归遍历,生成形如根(左子树)(右子树)
的字符串,确保结构唯一性。 -
哈希表记录频次
使用哈希表存储序列化字符串及其对应的子树根节点。当某个序列化字符串第二次出现时,判定为重复子树。 -
去重处理
使用集合存储结果,避免同一类重复子树被多次记录。
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
class Solution {
HashMap<String, TreeNode> map = new HashMap<>();
Set<TreeNode> ans = new HashSet<>();
public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
dfs(root);
return new ArrayList<>(ans);
}
private String dfs(TreeNode root) {
if (root == null) return "";
StringBuilder sb = new StringBuilder();
sb.append(root.val);
sb.append("(");
sb.append(dfs(root.left));
sb.append(")(");
sb.append(dfs(root.right));
sb.append(")");
String cur = sb.toString();
if (map.containsKey(cur)) {
ans.add(map.get(cur));
} else {
map.put(cur, root);
}
return cur;
}
}
🔍 复杂度分析
-
时间复杂度:O(N²),每个节点需序列化其所有子节点。
-
空间复杂度:O(N²),哈希表存储所有子树序列化字符串。
💡 关键点解析
-
序列化设计
-
使用
根(左子树)(右子树)
格式确保结构唯一性。 -
空节点用空字符串表示,避免歧义。
-
-
哈希表去重
-
当同一序列化字符串首次出现时存入哈希表。
-
后续重复出现时,将首次记录的根节点加入结果集。
-
-
后序遍历优化
-
递归过程本质是后序遍历(先处理左右子树,再处理当前节点),确保子树序列化完整。
-
🚀 拓展思考
-
三元组优化
可引入唯一ID标识子树,用(根值, 左ID, 右ID)
代替长字符串,优化时间和空间复杂度至 O(N)。
📝 总结
通过序列化子树为唯一字符串,并结合哈希表记录出现频次,能够高效解决二叉树重复子树的查找问题。该解法思路清晰,代码简洁,适合作为面试快速解题方案。