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


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

    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

  • 相关阅读:
    win10 系统下myeclipse 下启动 tomcat7 所遇到的问题
    搭建个人博客&论坛(LAMP):wordpress、discuz、phpMyAdmin
    拟合优度检验和独立性检验
    作业day19
    numpy
    正则模块
    模块功能
    模块
    匿名函数、异常处理
    递归和迭代器
  • 原文地址:https://www.cnblogs.com/feng-ying/p/15143870.html
Copyright © 2020-2023  润新知