第三章:用各种遍历框架序列化和反序列化二叉树

labuladong 二叉树的序列化

297. 二叉树的序列化与反序列化

  • 序列化

    • 前序遍历:(1)注意递归的出口;(2)将前序遍历的根节点数据保存,然后递归左右子树
  • 反序列化

    • (1)根据','把数据分开;
    • (2)pop出第0个数据,并建立根节点;
    • (3)递归剩下的数据 根据树的递归性质,第0个元素就是一棵树的根节点,所以只要将列表的这个元素取出作为根节点,剩下的交给递归去解决即可

二叉树的遍历:该题也可以用后续遍历及层序遍历解决(不可以用中序,因为中序的话,不知道根节点在哪个位置)

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Codec:

    def serialize(self, root):
        """Encodes a tree to a single string.

        :type root: TreeNode
        :rtype: str
        """
        self.res=''
        def dfs(root):
            # 递归出口:递归结束的条件,将'None,'保存到结果中
            if root==None:
                self.res+='None,'
                return
            # 前序遍历:中
            self.res+=str(root.val)+','
            ## 前序遍历:左右
            dfs(root.left)
            dfs(root.right)
        dfs(root)
        return self.res

    def deserialize(self, data):
        """Decodes your encoded data to tree.

        :type data: str
        :rtype: TreeNode
        """
        # 先把数据根据','分开
        data=data.split(',')
        # 递归
        def dfs():
            # 记录pop出的数据(也是根节点对应数据)
            rootData=data.pop(0)
            # 递归出口
            if rootData=='None':
                return None
            # 创建根节点
            root=TreeNode(int(rootData))
            # 递归剩下的数据
            root.left=dfs()
            root.right=dfs()
            return root
        return dfs()

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

DFS
序列化
递归的第一步都是特例的处理,因为这是递归的中止条件:如果根节点为空,返回”null“
序列化的结果为:根节点值 + “,” + 左子节点值(进入递归) + “,” + 右子节点值(进入递归)
递归就是不断将“根节点”值加到结果中的过程
反序列化
先将字符串转换成队列(python转换成列表即可)
接下来就进入了递归
i. 弹出左侧元素,即队列出队
ii. 如果元素为“null”,返回null(python返回None)
iii. 否则,新建一个值为弹出元素的新节点
iv. 其左子节点为队列的下一个元素,进入递归;右子节点为队列的下下个元素,也进入递归
v. 递归就是不断将子树的根节点连接到父节点的过程

class Codec:

    def serialize(self, root):
        """Encodes a tree to a single string.

        :type root: TreeNode
        :rtype: str
        """
        if not root:
            return 'None'
        return str(root.val) + ',' + str(self.serialize(root.left)) + ',' + str(self.serialize(root.right))

    def deserialize(self, data):
        """Decodes your encoded data to tree.

        :type data: str
        :rtype: TreeNode
        """
        def dfs(dataList):
            val = dataList.pop(0)
            if val == 'None':
                return None
            root = TreeNode(int(val))
            root.left = dfs(dataList)
            root.right = dfs(dataList)
            return root

        dataList = data.split(',')
        return dfs(dataList)

序列化与反序列化 C++

  • 前序、后序、层序都能够很好的找到根节点,而且不同于一般构造二叉树的问题,此处将空指针的地方指出,相当于比较完整的给出了树的结构。

  • 反序列化时,在确定根节点后,紧接着确定某一子树,对当前子树采用递归,形成了相同的问题,类似于序列化过程中的递归问题。

  • 反序列化中值得注意的是,前序是利用序列化结果,依次从前往后构造节点,类似于队列;后序依次从后往前,类似于栈;

  • 中序时,由于无法从序列化结果中找到根节点位置,也就无法准确划分左右子树,无法形成递归的结构,这种方式不能反序列化!

  • 层序遍历思路类似,相对要麻烦一点点点,拿新结点一层一层的往后面接,这里可以考虑直接用vector加上索引(也可以用queue,但是vector访问简洁一点)

c++没有像python那样的split()函数,所以要自己写该函数来处理数据

class Codec {
private:
    string res="";
public:
//    序列化
//    前序遍历
    void dfs(TreeNode* root){
//    递归出口
        if (root==nullptr){
            res+="NULL,";
            return;
        }
//        前序:中
        res+=(to_string(root->val))+",";
//        前序:左右
        dfs(root->left);
        dfs(root->right);
    }

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        dfs(root);
        res.pop_back();// 去掉结尾的","
        return res;
    }
///////////////////////////////////////////////////////
//反序列化
//    将string类的数据根据','进行split
    queue<string> split(string& data){
        queue<string> q;
        int start=0; // 如:123,456,789中,start表示每个数字开始的地方
//        注意这里的   i <= data.size()  和  if (i == data.size() ||data[i]==',')
//        1,2,3,4,5,6   最后的6后面没有逗号,但我们要把6push进去,因此要 取等于号,且if里面有 ||(或)
        for (int i = 0; i <= data.size(); ++i) {
            if (i == data.size() ||data[i]==','){
                q.push(data.substr(start,i-start));
                start=i+1;
            }
        }
        return q;
    }
//    对queue<string>类型的数据用递归
    TreeNode* deserializeQueue(queue<string>& q){
        string rootData=q.front();
        q.pop(); // 队列的pop是pop最开头的元素
        if (rootData=="NULL"){
            return nullptr;
        }
        TreeNode* root=new TreeNode(stoi(rootData));
        root->left=deserializeQueue(q);
        root->right=deserializeQueue(q);
        return root;
    }

    // Decodes your encoded data to tree.
//    调用上面的split函数和deserializeQueue函数
    TreeNode* deserialize(string data) {
        queue<string> Q= split(data);
        return deserializeQueue(Q);
    }
};

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

   转载规则


《第三章:用各种遍历框架序列化和反序列化二叉树》 M 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
第三章:Git原理之最近公共祖先 第三章:Git原理之最近公共祖先
labuladong Git原理之最近公共祖先 遇到任何递归类型的问题,无非就是“灵魂三问” 这个函数是干什么的?(不要跳进递归,没用。) 这个函数参数中的变量是什么? 得到函数的递归结果,你应该干什么 236. 二叉树的最近公共祖
2021-07-22
下一篇 
读“自学是门手艺” 读“自学是门手艺”
自学是门手艺 你一定要想办法启动自学,否则你没有未来; 你把自学当作一门手艺,长期反复磨练它; 你懂得学、练、用、造各个阶段之间的不同,以及针对每个阶段的对应策略; 面对 “过早引用” 过多的世界,你有你的应对方式; 你会 “囫囵吞枣”,
2021-07-18
  目录