Find duplicate subtrees
Contents
Question description
Given the root node root of a binary tree, return all duplicate subtrees.
For duplicate subtrees of the same type, you only need to return the root node of any one of them.
If two trees have the same structure and the same node values, they are considered duplicates.
https://assets.leetcode.com/uploads/2020/08/16/e1.jpg
Input: root = [1,2,3,4,null,2,4,null,null,4] Output: [[2,4],[4]]
Problem-solving ideas
Use postorder traversal + hashing!
Idea:
- Give each subtree an “ID card” (a string traversed in post-order)
- Record the number of occurrences of each subtree (hash, counter)
**Why does this method work? **
String description uniquely determines the subtree structure For example,
"2,4,#,#,#"can only correspond to one binary tree (root->left->right)Counter accurately captures repetitions When the same string appears a second time, it means that the subtree with exactly the same structure appears twice.
**1. Why do you think of using strings to represent subtrees? **
- Intuitive requirements: To determine whether two subtrees are the same, you need to compare their complete structures (including node values and left and right subtrees).
- Characteristics of strings:
A string can uniquely encode the structure of a tree (for example
"2,#,#"can only correspond to a subtree of single node 2). - Natural adaptation of recursion: Tree traversal itself is recursive, and string concatenation is naturally suitable for recursion (left + right + root = complete structure).
**2. Why use postorder traversal? **
- Postorder (left→right→root) order:
Only by first knowing the serialization results of the left and right subtrees can the string of the current subtree be spliced.
(For example, you must first know
4,#,#and#before you can spell2,4,#,#,#)
**3. Why use a hash table (or counter)? **
- Quick judgment: When the serialized string appears for the second time, it means that a subtree with the same structure has been encountered. (The hash table can determine whether it is repeated in O(1) time, which is much more efficient than brute force traversal)
**4. Why should empty nodes be represented by #? **
- Avoid ambiguity:
For example, the left side of the subtree
2is empty, the right side is3, and the serialization is"2,#,3,#,#"; If#is not used,"2,3"may be mistaken for2and the left side is3.
5. Source of inspiration for this method
- “Serialization” in the database: Similar to converting complex data into string storage (such as JSON).
- “Syntax tree” in the compiler: When the code is compiled, string hashing is used to optimize the detection of repeated structures in the syntax tree.
- Natural human thinking:
Imagine you were trying to describe the structure of a tree to someone. What would you say?
→ “The root is 2, the left is 4, and the right is empty” → Corresponds exactly to
"2,4,#,#,#"!
Code implementation
class Solution {
public:
vector<string> all_subtrees; // Store serialized strings of all subtrees
unordered_map<string, int> count; // Record how many times each serialization appears
vector<TreeNode*> result; // Store root nodes of duplicate subtrees
string serialize(TreeNode* node) {
if (node == nullptr) return "#";
string left = serialize(node->left);
string right = serialize(node->right);
// Serialized representation of the current subtree
string subtree = to_string(node->val) + "," + left + "," + right;
// Check whether it has appeared before
if (count[subtree] == 1) {
result.push_back(node);
}
count[subtree]++;
return subtree;
}
vector<TreeNode*> findDuplicateSubtrees(TreeNode* root) {
serialize(root);
return result;
}
};