深入理解二叉树的序列化和反序列化---剑指offer-JZ37 序列化二叉树

目录

引言

问题描述

题解概述

题解

理解难点

反序列化详解

index详解

1. 跟踪字符串中的位置

2. 递归函数中的状态保持

3. 区分节点值

4. 管理终止条件

重点


本篇题解完全参照官方,总结了一些个人感悟。

引言

在计算机科学中,序列化和反序列化是非常重要的概念。它们允许我们将复杂的数据结构如二叉树转换成字符串形式,以便于存储和传输,然后再从字符串还原回原始结构。本文将通过一个具体的例子——序列化和反序列化二叉树——来探讨这一过程,并重点解释反序列化的部分。

题目链接:序列化二叉树_牛客题霸_牛客网 (nowcoder.com)

问题描述

我们需要实现两个函数,一个用来序列化二叉树,另一个用来反序列化二叉树。序列化的目标是将二叉树转换为字符串形式,而反序列化的目标则是从字符串中重建原始的二叉树。

题解概述

序列化过程采用前序遍历,将每个节点的值转换为字符串,并用特殊字符(如 '#')表示空节点。反序列化过程则根据这个字符串重新构建出原始的二叉树结构。

题解

import java.util.*;
public class Solution {
    
    //处理序列化的功能函数(递归)
    private void SerializeFunction(TreeNode root, StringBuilder str){
        //如果节点为空,表示左子节点或右子节点为空,用#表示
        if(root == null){
            str.append('#');
            return;
        }
        //根节点
        str.append(root.val).append('!');
        //左子树
        SerializeFunction(root.left, str); 
        //右子树
        SerializeFunction(root.right, str);
    }
    
    public String Serialize(TreeNode root) {
        //处理空树
        if(root == null) 
            return "#";
        StringBuilder res = new StringBuilder();
        SerializeFunction(root, res);
        //把str转换成char
        return res.toString();
    }

    //序列的下标
    public int index = 0; 

    //处理反序列化的功能函数(递归)
    private TreeNode DeserializeFunction(String str){
        //到达叶节点时,构建完毕,返回继续构建父节点
        //空节点
        if(str.charAt(index) == '#'){ 
            index++;
            return null;
        }
        //数字转换
        int val = 0;
        //遇到分隔符或者结尾
        while(str.charAt(index) != '!' && index != str.length()){ 
            val = val * 10 + ((str.charAt(index)) - '0');
            index++;
        }
        TreeNode root = new TreeNode(val);
        //序列到底了,构建完成
        if(index == str.length()) 
            return root;
        else
            index++;
        //反序列化与序列化一致,都是前序
        root.left = DeserializeFunction(str);  
        root.right = DeserializeFunction(str);
        return root;
    }
    
    public TreeNode Deserialize(String str) {
        //空序列对应空树
        if(str == "#") 
            return null;
        TreeNode res = DeserializeFunction(str);
        return res;
    }
}

理解难点

在解决这个问题的过程中,很多人(包括我自己)都在反序列化的某些部分遇到了困难。特别是字符串到整数的转换和递归过程的理解。让我们一步步解开这些难点。

反序列化详解

反序列化的核心在于根据序列化字符串中的信息,逐步重建二叉树。

  1. 字符串解析

    • 反序列化过程从解析序列化字符串开始,我们逐个字符地读取字符串,遇到数字字符时需要将其转换为整数。

  2. 字符转整数

    • 这个转换过程可能是最容易引起困惑的。每当我们遇到一个数字字符,我们实际上是在处理一个多位数的一部分。例如,字符串 "123!" 中的 '1''2''3' 需要被组合成整数 123

    • 我们通过 val = val * 10 + (str.charAt(index) - '0') 实现这一点。每个字符被转换为相应的数字(例如,'1' 转换为 1),然后通过乘以 10 和累加的方式组合成完整的数字。

  3. 递归构建树

    • 一旦我们得到一个节点的值,我们就创建一个新的树节点,并递归地对字符串的其余部分进行同样的处理,构建这个节点的左子树和右子树。

           root.left = DeserializeFunction(str); 
           root.right = DeserializeFunction(str);

index详解

在反序列化字符串以重建二叉树的过程中,index 变量是一个关键的组件,使用 index 变量是为了跟踪当前处理的字符位置。它使我们能够在递归调用中正确地跟踪和管理处理进度,确保每个节点被正确地解析和构建。缺少这样的机制,我们将无法在递归过程中保持对当前处理位置的跟踪,从而无法正确地重建二叉树。让我们详细探讨其作用和重要性:

1. 跟踪字符串中的位置

在反序列化过程中,我们需要逐个字符地读取序列化字符串,以便重建二叉树。index 变量充当一个指针,指示当前正在处理的字符的位置。这是必要的,因为我们需要知道在任何给定时刻,我们正在处理字符串中的哪个部分。

2. 递归函数中的状态保持

由于反序列化是通过递归实现的,每次递归调用都会处理字符串的一部分。在递归调用中,没有一个内置的方式来自动跟踪上一次调用处理到的位置。因此,index 变量在递归调用之间维持了状态,确保每次递归调用都从上次停止的地方继续处理字符串。

3. 区分节点值

在序列化的字符串中,节点值可能由多个字符组成(例如,节点值为 12 时,由字符 '1' 和 '2' 表示)。index 变量帮助我们在处理多字符节点值时移动到正确的位置,确保我们能正确地解析出每个节点的完整值。

4. 管理终止条件

在反序列化过程中,当我们达到字符串的末尾时,需要一个机制来告知我们处理已完成。index 变量在这里也起到作用,当它等于字符串的长度时,表示我们已经处理完整个字符串。

重点

  • 递归的理解:在递归过程中,每次函数调用都处理字符串的一部分,并创建树的一个节点。通过维护全局 index 变量,我们确保每个递归调用都能正确地从上一个调用结束的地方继续。

  • 全局索引的作用index 变量在整个反序列化过程中起到关键作用,它帮助我们在字符串中的正确位置进行读取和解析。

  • 23
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值