# 剑指 Offer 37. 序列化二叉树

1.题目

剑指 Offer 37. 序列化二叉树
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。

请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。

示例: 

你可以将以下二叉树:

    1
   / \
  2   3
     / \
    4   5

序列化为 "[1,2,3,null,null,4,5]"
提示: 这与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。

说明: 不要使用类的成员 / 全局 / 静态变量来存储状态,你的序列化和反序列化算法应该是无状态的。

2.自我思路及实现

序列化:层序遍历BFS
首先看到要求返回的字符串是一个字符串数组
只对完全二叉树可以实现的代码
序列化:层序遍历,使用队列实现,因为输出是个字符串,使用StringBuffer不断添加元素。但可以发现结尾的"null"是不添加的,因此记录下最后一个不为空的节点的位置,然后删去之后的字符。
反序列化:首先处理掉开头和结尾的括号,将其按逗号分割为字符串数组。对于完全二叉树,左子节点角标 = 父节点角标*2 +1,右子节点角标 = 父节点角标 *2 +2

时间:序列化N,反序列化N
空间:序列化N, 反序列化N

存在的问题:

  • 反序列化时将字符串转化为字符数组操作过于繁琐
  • 反序列化不能构建其他二叉树
  • 结尾的"null"其实是否省略对本题没有什么影响,因为不限定序列化的方式,只要能还原即可
public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if(root == null)
            return "[]";

        List<String> list = new ArrayList<>();
        StringBuffer sb = new StringBuffer();
        Queue<TreeNode> queue = new LinkedList<>();

        sb.append("[");
        queue.offer(root);
        int end = 0;  //记录最后一个不为空的节点位置

        while(!queue.isEmpty())
        {
            TreeNode temp = queue.poll();
            if(temp == null)
            {
                sb.append("null");
            }
            else
            {
                sb.append(""+temp.val);
                queue.offer(temp.left);
                queue.offer(temp.right);
                end = sb.length();
            }
            sb.append(",");       
        }
        sb.replace(end, sb.length(), ""); //将多余null和逗号去掉
        sb.append("]");
        return sb.toString();  
        
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if(data.equals("[]"))
            return null;
        StringBuffer sb = new StringBuffer(data);
        sb.replace(0,1,"");
        sb.replace(sb.length() - 1, sb.length(), "");
        String[] s = sb.toString().split(",");
        
        TreeNode root = new TreeNode(Integer.parseInt(s[0]));

        return build(s, 0);
        
    }
    TreeNode build(String[] s,int index)
    {
        if(index >= s.length ||  s[index].equals("null"))
            return null;
        TreeNode root = new TreeNode(Integer.parseInt(s[index]));

        //System.out.println(root.val);

        TreeNode subleft = build(s, index*2 + 1);
        TreeNode subright = build(s, index * 2 + 2); 

        if(subleft != null)
            root.left = subleft;
        if(subright != null)
        root.right = subright;

        return root;

    }
    
}

3.总结思路及实现

DFS+队列
序列化思路为正常的层序遍历,和上述思路相同
反序列化:
直接使用创建子串的方式去掉括号
使用队列来存储每个可以成为父节点的节点,第一个父节点是根

实现思路:

  • 创建父亲队列和根节点
  • 定义一个布尔型的变量isLeft,用来标记左右节点,初始化为true
  • 第一个父亲是根节点
  • 对字符串数组进行遍历
    创建当前节点("null"时为空节点)
    判断该节点是左子节点还是右子节点并连接到父亲节点
    判断节点是否为空,不为空则加入父亲队列
    改变isLeft的值,进行下一个节点
    当该节点为左子节点时,要更换父亲节点
  • 结束循环,返回根节点

时间:序列化N,反序列化N
空间:序列化N, 反序列化N

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if(root == null)
            return "[]";

        List<String> list = new ArrayList<>();
        StringBuffer sb = new StringBuffer();
        Queue<TreeNode> queue = new LinkedList<>();

        sb.append("[");
        queue.offer(root);
        int end = 0;  //记录最后一个不为空的节点位置

        while(!queue.isEmpty())
        {
            TreeNode temp = queue.poll();
            if(temp == null)
            {
                sb.append("null");
            }
            else
            {
                sb.append(temp.val+"");
                queue.offer(temp.left);
                queue.offer(temp.right);
                end = sb.length();
            }
            sb.append(",");       
        }
        sb.replace(end, sb.length(), ""); //将多余null和逗号去掉
        sb.append("]");
        return sb.toString();  
        
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if(data.equals("[]"))
            return null;
        String[] s = data.substring(1, data.length() - 1).split(",");
        Queue<TreeNode> parents = new LinkedList<>();
        TreeNode root = new TreeNode(Integer.parseInt(s[0]));

        boolean isLeft = true;
        TreeNode parent = root;
        for(int i = 1; i < s.length; i++)
        {
            TreeNode cur = s[i].equals("null") ? null : new TreeNode(Integer.parseInt(s[i]));
            if(isLeft)
                parent.left = cur;
            else
                parent.right = cur;
            
            if(cur != null)
                parents.offer(cur);
            
            isLeft = !isLeft;//节点改变
            if(isLeft)//右子节点的父节点与左子节点相同
            {
                parent = parents.poll();
            }
        }
        return root;
    }
    
}

// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));

4.相关知识

  • Integer:Integer.parseInt(String s):返回一个int型数字
    Integer.valueOf(String s):返回一个Integer对象,有个缓冲区性能更优
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值