给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。
两棵树重复是指它们具有相同的结构以及相同的结点值。
示例 1:
1 / \ 2 3 / / \ 4 2 4 / 4
下面是两个重复的子树:
2 / 4
和
4
因此,你需要以列表的形式返回上述重复子树的根结点。
思路:
我已开始的思路是采用后序遍历,每次遍历到一个节点就保存到一个map中
unordered_map<TreeNode*, int> m
保存的规则是如果对应的key相同(本意是key的val相同,但是map只能查找key相同的元素),就判断是否对应的节点的子树和root的子树是否相同,如果相同就把key对应的value+1,这样思考感觉是对的,其实是不对的!!!!因为如果map的key存的是TreeNode*,由于每一个TreeNode*分配都是在堆区,地址每个元素都是不一样的,遍历整棵树就不可能找到两个一模一样的key,虽然我们的本意是希望找到key对应的val相同的,于是修改代码,用两个map,一个是
unordered_map<TreeNode*, int> m
另一个是:
unordered_map<int,TreeNode*> va
通过va的key来保存TreeNode*对应的value,如果key相同则找到对应的value的TreeNode*,然后在加到m中。但是这样做也存在问题,如下图所示:
1 / \ 2 2 / / \ 3 4 2 / 4
va中先保存了key为2的红色的节点,当来到蓝色的节点2时,由于蓝色节点的子树和红色节点的子树不相同,于是不会加到m中,同样紫色的也不会计算到m中,所以这种解法只能保存第一次遇到的节点的值,以后再遇到只会和第一次保存的值比较,而不会出现第二次和第三次的比较,而结果很可能是第二次和第三次出现的相同。
这个就是博主的整道题思考过程,总之是希望找到一种结构,能够以当前节点的值来索引,不同节点如果相同值也能存储成不同的结构。所以直接参考了大神的discuss部分。
总的思路差不多,只不过用map来保存的结构变成了一棵树的前序遍历的序列化字符串比较,即:
unordered_map<string, int> m
key为当前节点的前序遍历的字符串序列化结果
value为对应序列出现的次数
在遍历过程中,如果value的值等于2,则把该节点的值加到res中,注意只加value==2的结果,避免重复添加。
代码如下:
string helper(TreeNode* root, unordered_map<string, int> &m, vector<TreeNode*> &res) {
if (!root) {
return "#";
}
string s = to_string(root->val) + "," + helper(root->left, m, res) + "," + helper(root->right, m, res);
m[s]++;
if (m[s] == 2) {
res.push_back(root);
}
return s;
}
vector<TreeNode*> findDuplicateSubtrees(TreeNode* root) {
vector<TreeNode*> res;
if (!root) {
return res;
}
unordered_map<string, int> m;
helper(root, m, res);
return res;
}