• 二叉树3


    二叉树3

    如何判断我们应该用前序还是中序还是后序遍历的框架

    思考一个二叉树节点需要做什么,到底用什么遍历顺序就清楚了

    leetcode652.寻找重复的子树

    image-20210206132816529
    List<TreeNode> findDuplicateSubtrees(TreeNode root);
    

    输入是一棵二叉树的根节点root,返回的是一个列表,里面装着若干个二叉树节点,这些节点对应的子树在原二叉树中是存在重复的。

    说起来比较绕,举例来说,比如输入如下的二叉树:
    image-20210206132929092

    节点 4 本身可以作为一棵子树,且二叉树中有多个节点 4:
    image-20210206132956180

    类似的,还存在两棵以 2 为根的重复子树:
    image-20210206133023319

    我们返回的List中就应该有两个TreeNode,值分别为 4 和 2(具体是哪个节点都无所谓)。

    这题咋做呢?还是老套路,先思考,对于某一个节点,它应该做什么

    比如说,你站在图中这个节点 2 上:
    image-20210206133050629

    如果你想知道以自己为根的子树是不是重复的,是否应该被加入结果列表中,你需要知道什么信息?

    你需要知道以下两点

    1、以我为根的这棵二叉树(子树)长啥样

    2、以其他节点为根的子树都长啥样

    好,那我们一个一个来解决,先来思考,我如何才能知道以自己为根的二叉树长啥样?其实看到这个问题,就可以判断本题要使用「后序遍历」框架来解决:

    void traverse(TreeNode root) {
        traverse(root.left);
        traverse(root.right);
        /* 解法代码的位置 */
    }
    

    很简单呀,我要知道以自己为根的子树长啥样,是不是得先知道我的左右子树长啥样,再加上自己,就构成了整棵子树的样子?

    如果你还绕不过来,我再来举个非常简单的例子:计算一棵二叉树有多少个节点。这个代码应该会写吧:

    int count(TreeNode root) {
        if (root == null) {
            return 0;
        }
        // 先算出左右子树有多少节点
        int left = count(root.left);
        int right = count(root.right);
        /* 后序遍历代码位置 */
        // 加上自己,就是整棵二叉树的节点数
        int res = left + right + 1;
        return res;
    }
    

    明确了要用后序遍历,那应该怎么描述一棵二叉树的模样呢?
    二叉树的前序/中序/后序遍历结果可以描述二叉树的结构
    所以,我们可以通过拼接字符串的方式把二叉树序列化:

    String traverse(TreeNode root) {
        // 对于空节点,可以用一个特殊字符表示
        if (root == null) {
            return "#";
        }
        // 将左右子树序列化成字符串
        String left = traverse(root.left);
        String right = traverse(root.right);
        /* 后序遍历代码位置 */
        // 左右子树加上自己,就是以自己为根的二叉树序列化结果
        String subTree = left + "," + right + "," + root.val;
        return subTree;
    }
    

    我们用非数字的特殊符#表示空指针,并且用字符,分隔每个二叉树节点值,这属于序列化二叉树的套路了,不多说。

    注意我们subTree是按照左子树、右子树、根节点这样的顺序拼接字符串,也就是后序遍历顺序。你完全可以按照前序或者中序的顺序拼接字符串,因为这里只是为了描述一棵二叉树的样子,什么顺序不重要。

    这样,我们第一个问题就解决了,对于每个节点,递归函数中的subTree变量就可以描述以该节点为根的二叉树

    现在我们解决第二个问题,我知道了自己长啥样,怎么知道别人长啥样?这样我才能知道有没有其他子树跟我重复对吧。

    这很简单呀,我们借助一个外部数据结构,让每个节点把自己子树的序列化结果存进去,这样,对于每个节点,不就可以知道有没有其他节点的子树和自己重复了么?可以使用hashmap

    // 记录所有子树以及出现的次数
    HashMap<String, Integer> memo = new HashMap<>();
    // 记录重复的子树根节点
    LinkedList<TreeNode> res = new LinkedList<>();
    
    /* 主函数 */
    List<TreeNode> findDuplicateSubtrees(TreeNode root) {
        traverse(root);
        return res;
    }
    
    /* 辅助函数 */
    String traverse(TreeNode root) {
        if (root == null) {
            return "#";
        }
    
        String left = traverse(root.left);
        String right = traverse(root.right);
    
        String subTree = left + "," + right+ "," + root.val;
    
        int freq = memo.getOrDefault(subTree, 0);
        // 多次重复也只会被加入结果集一次
        if (freq == 1) {
            res.add(root);
        }
        // 给子树对应的出现次数加一
        memo.put(subTree, freq + 1);
        return subTree;
    }
    
  • 相关阅读:
    SQL 操作结果集 -并集、差集、交集、结果集排序
    MongoDB系列四:解决secondary的读操作
    org.apache.hadoop.ipc.RemoteException: User: root is not allowed to impersonate root
    hive 中窗口函数row_number,rank,dense_ran,ntile分析函数的用法
    FormData上传文件同时附带其他参数
    Hive删除分区
    Hive日期格式转换用法
    HIVE 不支持group by 别名
    ODS与EDW的区别
    hive数据类型转换、字符串函数、条件判断
  • 原文地址:https://www.cnblogs.com/shiji-note/p/14383111.html
Copyright © 2020-2023  润新知