剑指Offer面试题-37 序列化二叉树(二叉树、序列化)

题目描述

请实现两个函数,分别用来序列化和反序列化二叉树。

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

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {

    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {

    }
}

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

题解

使用先序遍历的方式,将二叉树转换成字符串,将节点中的值用逗号,隔开。如果遇到null,则用$替代。

例如这棵树:

    1
   / \
  2   3
     / \
    4   5

序列化后的字符串是:1,2,$,$,3,4,$,$,5,$,$

代码实现:

// Encodes a tree to a single string.
public String serialize(TreeNode root) {
    StringBuilder sb = new StringBuilder();
    preOrder(root, sb);
    return sb.toString();
}

// 先序遍历
private void preOrder(TreeNode node, StringBuilder sb)
{
    if (node == null) {
        sb.append('$');
        return;
    }
    sb.append(node.val);
    sb.append(',');
    preOrder(node.left, sb);
    preOrder(node.right, sb);
}

然后用相同的方法根据字符串重建树。递归思路可能比较抽象,分析几个例子就能明白。

定义一个变量index来标识字符串目前读到哪个位置了。

  • 如果当前读到的字符是$,说明是一个空指针,直接返回null。
  • 如果当前读到的是一个非$字符,那么就说明是数字,读取整个数字,并用其值创建对象,递归读取字符串给左子树赋值和给右子树赋值。

代码实现:

// 全局变量,用来标识字符串读到哪个位置了
private int index;

// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
    if (data.charAt(index) == '$') {
        index++;
        return null;
    }
    // 读取一个数字
    int val = readNumber(data);
    // 用读到的数字创建对象
    TreeNode node = new TreeNode(val);
    node.left = deserialize(data);
    node.right = deserialize(data);
    return node;
}
// 读取一个数字,如10,-108,5
private int readNumber(String data)
{
    int val = 0;
    // 正负号
    int symbol = 1;
    while (data.charAt(index) != ',')
    {
        if (data.charAt(index) == '-') {
            symbol = -1;
        } else {
            val = val * 10 + data.charAt(index) - '0';
        }
        index++;
    }
    index++; // 跳过逗号
    return val * symbol;
}

这种顺序读取可行的原因是:先序遍历是先遍历根节点,其次左叶子节点,最后右叶子节点。按照同样的顺序就能处理。

同时,不需要做index超界判断,因为index走到String最后一个字符后,反序列化函数就刚好重构完二叉树。(这里不考虑字符串非法问题,因为字符串是由序列化函数生成的,所以认为是合法的)

完整代码:

public class Codec {
    private int index;

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        StringBuilder sb = new StringBuilder();
        preOrder(root, sb);
        return sb.toString();
    }

    private void preOrder(TreeNode node, StringBuilder sb)
    {
        if (node == null) {
            sb.append('$');
            return;
        }
        sb.append(node.val);
        sb.append(',');
        preOrder(node.left, sb);
        preOrder(node.right, sb);
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if (data.charAt(index) == '$') {
            index++;
            return null;
        }
        // 读取一个数字
        int val = readNumber(data);
        TreeNode node = new TreeNode(val);
        node.left = deserialize(data);
        node.right = deserialize(data);
        return node;
    }

    private int readNumber(String data)
    {
        int val = 0;
        // 正负号
        int symbol = 1;
        while (data.charAt(index) != ',')
        {
            if (data.charAt(index) == '-') {
                symbol = -1;
            } else {
                val = val * 10 + data.charAt(index) - '0';
            }
            index++;
        }
        index++; // 跳过逗号
        return val * symbol;
    }
}

时间复杂度: O ( N ) O(N) O(N)
N N N为树的节点个数,每个节点遍历一次即可。

空间复杂度: O ( N ) O(N) O(N)
序列化字符串的长度和树节点个数呈线性关系。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值