二叉树序列化和反序列化

二叉树的序列化和反序列化

1. 二叉树序列化:

(a) 如果当前的树为空,则表示成X

(b) 如果当前的树不为空,则表示成(<left_sub_tree>)cur_num(<right_sub_tree>),其中,

<left_sub_tree>: 是左子树序列化后的结果

<right_sub_tree>: 是右子树序列化后的结果

cur_num: 当前节点的值

中序遍历可以得到序列化的结果。

2. 二叉树的反序列化

根据二叉树序列化,我们可以推导出这样的巴科斯范式:

T -> (T) num (T) | x

用T代表一棵树序列化的结果,| 表示T构成的为(T) num (T) 或者X,| 左边是对T的递归定义,右边规定了递归终止的边界条件。

T 的定义中,序列中的第一个字符要么是 X,要么是 (,所以这个定义是不含左递归的,当我们开始解析一个字符串的时候,如果开头是 X,我们就知道这一定是解析一个「空树」的结构,如果开头是 (,我们就知道需要解析 (T) num (T) 的结构,因此这里两种开头和两种解析方法一一对应,可以确定这是一个无二义性的文法

我们只需要通过开头的第一个字母是 X 还是 ( 来判断使用哪一种解析方法,所以这个文法是 LL(1) 型文法,如果你不知道什么是 LL(1) 型文法也没有关系,你只需要知道它定义了一种递归的方法来反序列化,也保证了这个方法的正确性——我们可以设计一个递归函数:

这个递归函数传入两个参数,带解析的字符串和当前当解析的位置 ptr,ptr 之前的位置是已经解析的,ptr 和 ptr 后面的字符串是待解析的。如果当前位置为 X 说明解析到了一棵空树,直接返回。否则当前位置一定是 (,对括号内部按照 (T) num (T) 的模式解析

class Codec {

public:

    string serialize(TreeNode* root) {

        if (!root) {

            return "X";

        }

        auto left = "(" + serialize(root->left) + ")";

        auto right = "(" + serialize(root->right) + ")";

        return left + to_string(root->val) + right;

    }

    inline TreeNode* parseSubtree(const string &data, int &ptr) {

        ++ptr; // 跳过左括号

        auto subtree = parse(data, ptr);

        ++ptr; // 跳过右括号

        return subtree;

    }

    inline int parseInt(const string &data, int &ptr) {

        int x = 0, sgn = 1;

        if (!isdigit(data[ptr])) {

            sgn = -1;

            ++ptr;

        }

        while (isdigit(data[ptr])) {

            x = x * 10 + data[ptr++] - '0';

        }

        return x * sgn;

    }

    TreeNode* parse(const string &data, int &ptr) {

        if (data[ptr] == 'X') {

            ++ptr;

            return nullptr;

        }

        auto cur = new TreeNode(0);

        cur->left = parseSubtree(data, ptr);

        cur->val = parseInt(data, ptr);

        cur->right = parseSubtree(data, ptr);

        return cur;

    }

    TreeNode* deserialize(string data) {

        int ptr = 0;

        return parse(data, ptr);

    }

};

3.复杂度分析

时间复杂度:序列化时做了一次遍历,渐进时间复杂度为 O(n)O(n)。反序列化时,在解析字符串的时候 ptr 指针对字符串做了一次顺序遍历,字符串长度为 O(n)O(n),故这里的渐进时间复杂度为 O(n)O(n)。

空间复杂度:考虑递归使用的栈空间的大小,这里栈空间的使用和递归深度有关,递归深度又和二叉树的深度有关,在最差情况下,二叉树退化成一条链,故这里的渐进空间复杂度为 O(n)O(n)。

摘自:https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree/solution/er-cha-shu-de-xu-lie-hua-yu-fan-xu-lie-hua-by-le-2

原文地址:https://www.cnblogs.com/feng-ying/p/15143870.html