leetcode 652. Find Duplicate Subtrees(找出重复的子树)

Given the root of a binary tree, return all duplicate subtrees.

For each kind of duplicate subtrees, you only need to return the root node of any one of them.

Two trees are duplicate if they have the same structure with the same node values.

Example 1:
在这里插入图片描述
Input: root = [1,2,3,4,null,2,4,null,null,4]
Output: [[2,4],[4]]

给出一个二叉树,找出其中相同的子树,相同的子树是指相同的节点值,相同的结构,多个重复的只需返回一个,返回root节点即可。

思路:

如果遍历到每一个节点都可以返回这个节点下面的树结构和值的组合(具有唯一性),即可以迅速判断是不是同一棵子树。序列化有这个唯一性,可以用序列化解决。

1.用String保存序列化

树的结构需要区分root, 左子树,右子树,
其中值的部分用数字,然后用"#"分割root, 左子树和右子树:key = root.val # left # right
所以用树的后序遍历来获得左子树和右子树的序列化。

当key相同时,说明是相同的两个子树,记录下每个子树已经出现的次数。
出现第2次时证明相同,加入list。后面再出现时不需要再重复记录。

class Solution {
    public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
        HashMap<String, Integer> map = new HashMap<>();
        List<TreeNode> res = new ArrayList<>();
        postOrder(root, map, res);
        return res;
    }

    String postOrder(TreeNode root, HashMap<String, Integer> map, List<TreeNode> res) {
        if(root == null) return "";

        String left = postOrder(root.left, map, res);
        String right = postOrder(root.right, map, res);
        String key = left + "#" + right +"#" + root.val;
        int cnt = map.getOrDefault(key, 0);
        if(cnt == 1) res.add(root);
        map.put(key, cnt+1);
        return key;
    }
}

2.用long型保存序列化

用String的方法比较容易理解,但是不够快。
看到网上有个更快的方法,它是用long型保存一棵二叉树,其中高位32位到16位保存root,中间16位保存左子树,下面16位保存右子树,这样每个树可形成一个long型的key。
但是这里有个问题,方法1中作为key的String可以无限拉长,但是这里不行,如果root有很多子树,key多次左移位就会超出long型的范围。
要防止这种情况发生,就要每个key对应一个id, 移位的时候是id移位而不是key本身移位。
所以每个key又需要对应一个id,
同时还需要记录这个结构出现的次数,即id还要对应一个count。

所以需要两个hashMap,一个保存<key, id>, 一个保存<id, count>
root的id是0,[2,4]的id是1,[4]的id是2,当出现下一个[2,4]时,通过计算key找到相同的id是1,再通过id找到count,一旦count == 2,说明之前出现过一次,保存进结果即可,count>2的就不需要再保存了。

同时注意root的值保存进key时必须要cast到long型,因为它要左移32位,如果直接用int,会溢出。
另外count要先+1再判断是否 == 2. 而不是直接判断是不是 == 1,因为有些出现过1次但是和本次的key并不相同的也会算进去。

如果不是特别追求快(80%和98%的差别),建议方法1,更易于理解。

//6ms
    public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
        List<TreeNode> result = new ArrayList<>();
        if(root == null) return result;
        
        HashMap<Long, Integer> ids = new HashMap<>();
        HashMap<Integer, Integer> count = new HashMap();
        getId(root, ids, count, result);
        
        return result;
    }
    
    int getId(TreeNode root, HashMap<Long, Integer> ids,
             HashMap<Integer,Integer> count, List<TreeNode> result) {
        if(root == null) return 0;
        
        long key = ((long)root.val << 32 | getId(root.left, ids, count, result) << 16 |
            getId(root.right, ids, count, result));
        int id = 0;
        
        if(ids.get(key) != null) {
            id = ids.get(key);
        } else {
            id = ids.size() + 1;
        }
        
        ids.put(key, id);
        count.put(id, count.getOrDefault(id, 0)+1);
        if(count.get(id) == 2) {
            result.add(root);
        }
        
        return id;
    }

参考资料

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝羽飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值