刷题日记Day3 | 二叉树序列化

Posted 结构化思维wz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了刷题日记Day3 | 二叉树序列化相关的知识,希望对你有一定的参考价值。

如何判断我们应该用前序还是中序还是后序遍历的框架?根据题意,思考一个二叉树节点需要做什么,到底用什么遍历顺序就清楚了。

652.寻找重复的子树


输入是一棵二叉树的根节点root,返回的是一个列表,里面装着若干个二叉树节点,这些节点对应的子树在原二叉树中是存在重复的。
分析:
解决此问题需要知道两点:

  1. 以root为根的节点二叉树长什么样?
  2. 以其他节点为根的二叉树长什么样?
  3. 让每个节点把自己序列化的结果存进去就知道是否重复了。借助一个hashMap

解答:🚋(看答案出结果)

class Solution 
// 记录所有子树以及出现的次数
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); //如果存在subTree就使用它的value,如果不存在就使用0
    // 多次重复也只会被加入结果集一次
    if (freq == 1) 
        res.add(root);
    
    // 给子树对应的出现次数加一
    memo.put(subTree, freq + 1);
    return subTree;



297.二叉树的序列化和反序列化(困难)


函数声明:

public class Codec 

    // 二叉树-->字符串
    public String serialize(TreeNode root) 
        
    

    // 字符串--->二叉树
    public TreeNode deserialize(String data) 
        
    


前序遍历

public class Codec 

String SEP = ",";
String NULL = "#";

/* 主函数,将二叉树序列化为字符串 */
public String serialize(TreeNode root) 
    StringBuilder sb = new StringBuilder();
    serialize(root, sb);
    return sb.toString();


/* 辅助函数,将二叉树存入 StringBuilder */
void serialize(TreeNode root, StringBuilder sb) 
    if (root == null) 
        sb.append(NULL).append(SEP);
        return;
    

    /****** 前序遍历位置 ******/
    sb.append(root.val).append(SEP);
    /***********************/

    serialize(root.left, sb);
    serialize(root.right, sb);



/* 主函数,将字符串反序列化为二叉树结构 */
TreeNode deserialize(String data) 
    // 将字符串转化成列表
    LinkedList<String> nodes = new LinkedList<>();
    for (String s : data.split(SEP)) 
        nodes.addLast(s);
    
    return deserialize(nodes);


/* 辅助函数,通过 nodes 列表构造二叉树 */
TreeNode deserialize(LinkedList<String> nodes) 
    if (nodes.isEmpty()) return null;

    /****** 前序遍历位置 ******/
    // 列表最左侧就是根节点
    String first = nodes.removeFirst();
    if (first.equals(NULL)) return null;
    TreeNode root = new TreeNode(Integer.parseInt(first));
    /***********************/

    root.left = deserialize(nodes);
    root.right = deserialize(nodes);

    return root;



后序遍历

/* 主函数,将二叉树序列化为字符串 */
public String serialize(TreeNode root) 
    StringBuilder sb = new StringBuilder();
    serialize(root, sb);
    return sb.toString();

/* 辅助函数,将二叉树存入 StringBuilder */
void serialize(TreeNode root, StringBuilder sb) 
    if (root == null) 
        sb.append(NULL).append(SEP);
        return;
    

    serialize(root.left, sb);
    serialize(root.right, sb);

    /****** 后序遍历位置 ******/
    sb.append(root.val).append(SEP);
    /***********************/

反序列化:
deserialize 方法首先寻找 root 节点的值,然后递归计算左右子节点。

可见,root 的值是列表的最后一个元素。我们应该从后往前取出列表元素,先用最后一个元素构造 root,然后递归调用生成 root 的左右子树。注意,根据上图,从后往前在 nodes 列表中取元素,一定要先构造 root.right 子树,后构造 root.left 子树。

/* 主函数,将字符串反序列化为二叉树结构 */
TreeNode deserialize(String data) 
    LinkedList<String> nodes = new LinkedList<>();
    for (String s : data.split(SEP)) 
        nodes.addLast(s);
    
    return deserialize(nodes);


/* 辅助函数,通过 nodes 列表构造二叉树 */
TreeNode deserialize(LinkedList<String> nodes) 
    if (nodes.isEmpty()) return null;
    // 从后往前取出元素
    String last = nodes.removeLast();
    if (last.equals(NULL)) return null;
    TreeNode root = new TreeNode(Integer.parseInt(last));
    // 限构造右子树,后构造左子树
    root.right = deserialize(nodes);
    root.left = deserialize(nodes);

    return root;


层序遍历

  /**
     * 二叉树的序列化
     * 层序遍历
     */
    String SEP = ",";
    String NULL = "#";
    /*将二叉树序列化为字符串*/
    String serialize(TreeNode root)
        if(root == null)return "";
        StringBuilder sb = new StringBuilder();
        //初始化队列,将root加入队列
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        
        while(!q.isEmpty())
            TreeNode cur = q.poll();
            if(cur == null)
                sb.append(NULL).append(SEP);
                continue;
            
            sb.append(cur.val).append(SEP);
            q.offer(cur.left);
            q.offer(cur.right);
        
        return sb.toString();
    

/**
     * 反序列化
     */
    TreeNode deserialize(String data)
        if(data.isEmpty())return null;
        String[] nodes = data.split(SEP);
        //第一个元素就是root的值
        TreeNode root = new TreeNode(Integer.parseInt(nodes[0]));
        //队列q记录父节点,将root加入队列
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        for(int i = 1; i< nodes.length;i++)
            TreeNode parent =q.poll();
            //父节点对应的左侧节点的值
            String left = nodes[i++];
            if(!left.equals(NULL))
                parent.left = new TreeNode(Integer.parseInt(left));
                q.offer(parent.left);
            else 
                parent.left =null;
            
            String right = nodes[i++];
            if(!left.equals(NULL))
                parent.right = new TreeNode(Integer.parseInt(right));
                q.offer(parent.right);
            else 
                parent.right =null;
            
        
        return root;
    

以上是关于刷题日记Day3 | 二叉树序列化的主要内容,如果未能解决你的问题,请参考以下文章

刷题日记Day2 | 构造二叉树

LeetCode刷题日记精选例题(代码+链接)

刷题日记Day1 | 二叉树

java刷题--105从前序与中序遍历序列构造二叉树

剑指Offer 刷题 序列化二叉树

LeetCode刷题笔记-数据结构-day18