652. 寻找重复的子树

652. 寻找重复的子树

难度中等545收藏分享切换为英文接收动态反馈

给定一棵二叉树 root,返回所有重复的子树

对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。

如果两棵树具有相同的结构相同的结点值,则它们是重复的。

示例 1:

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

示例 2:

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

示例 3:

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

提示:

  • 树中的结点数在[1,10^4]范围内。
  • -200 <= Node.val <= 200

分析:胡汉三又回来写题了,好久不写题了变得好垃圾了QAQ,疯狂看题解。本题的题意就是输出所有重复子树的根节点,需要注意的是,对于一组重复的子树,输出其中任意一个根即可。

1.想判断两棵树相等,需要把两颗树都遍历一遍,然后看是否相同,后续还要进行去重。。。个屁啊,没事找事,是个思路但是妥妥不对劲 

2.针对看一下上述的优化点 ,实际上我们看完整棵树就理应知道所有的子结构了,另外判断结构相似可以比较两个结构的序列化结果;还没完,本题中强调了子树,子树的叶子在原树上也必须是叶子,而且我们很容易发现递归性质--要想判断大一点的结构是否相同,就要先看小一点的结构是否相同,而DFS的返回顺序正好就符合了这一条性质;现在我们来看看怎么去快速查找树中的相似子树结构,实际上上面也提到了,树的结构可以用序列化结果表示,那么我们只需要获取所有的子树,再使用他们的序列化结果进行查询即可。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:

    unordered_map<string, int> repeat_tree;

    string dfs(TreeNode* root, vector<TreeNode*> &nodes){
        if(!root){
            return "";
        }
        string tree_serial = to_string(root -> val) + "(" + dfs(root -> left, nodes) + ")" + "(" + dfs(root -> right, nodes) + ")";
        if(repeat_tree.find(tree_serial) == repeat_tree.end()){
            repeat_tree[tree_serial] = 1;
        } else {
            ++ repeat_tree[tree_serial];
            if(repeat_tree[tree_serial] == 2){
                nodes.emplace(nodes.end(), root);
            }
        }
        return tree_serial;
    }

    vector<TreeNode*> findDuplicateSubtrees(TreeNode* root) {
        vector<TreeNode*> nodes;
        dfs(root, nodes);
        return nodes;
    }
};

3.官网解法:用int序号替代string加速相等的判断

class Solution {
public:
    vector<TreeNode*> findDuplicateSubtrees(TreeNode* root) {
        dfs(root);
        return {repeat.begin(), repeat.end()};
    }

    int dfs(TreeNode* node) {
        if (!node) {
            return 0;
        }
        auto tri = tuple{node->val, dfs(node->left), dfs(node->right)};
        if (auto it = seen.find(tri); it != seen.end()) {//当前结构出现过
            repeat.insert(it->second.first);//repeat<string> 表示出现的重复串 插入根节点
            return it->second.second;//返回代表当前串的序号 用序号替代string加速匹配
        }
        else {//当前结构没出现过,加入seen集合
            seen[tri] = {node, ++idx};
            return idx;
        }
    }

private:
    static constexpr auto tri_hash = [fn = hash<int>()](const tuple<int, int, int>& o) -> size_t {
        auto&& [x, y, z] = o;
        return (fn(x) << 24) ^ (fn(y) << 8) ^ fn(z);
    };

    unordered_map<tuple<int, int, int>, pair<TreeNode*, int>, decltype(tri_hash)> seen{0, tri_hash};
    unordered_set<TreeNode*> repeat;
    int idx = 0;
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值