http://blog.csdn.net/qq_27703417/article/details/70958692
先序遍历二叉树,如果遇到空节点,就在str的末尾加上“#!”,“#”表示这个节点为空,节点值不存在,当然你也可以用其他的特殊字符,“!”表示一个值的结束。如果遇到不为空的节点,假设节点值为3,就在str的末尾加上“3!”。现在请你实现树的先序序列化。
先序遍历
- import java.util.*;
- //使用递归先序遍历对二叉树进行序列化
- public class TreeToString {
- public String toString(TreeNode root) {
- //注意:Java中String是不可改变的,不能进行引用传递,改为使用StringBuffer
- StringBuilder res=new StringBuilder("");
- //调用递归方法完成二叉树遍历序列化
- this.preOrder(root,res);
- //返回结果
- return res.toString();
- }
- //递归方法,用来先序遍历二叉树同时将其序列化为字符串
- private void preOrder(TreeNode root, StringBuilder str){
- //递归结束的边界条件
- if(root==null){
- str.append("#!");
- return;
- }
- //先遍历根结点
- str.append(root.val+"!");
- //遍历左子树
- this.preOrder(root.left,str);
- //遍历右子树
- this.preOrder(root.right,str);
- }
- }
反序列化
所谓反序列化是根据一个字符串重新建立一棵二叉树,反序列化是序列化的逆过程,对于一个字符串,首先按照分隔符!将其分割为字符串数组,每个字符串元素代表一个结点,然后开始重建二叉树。由于每个结点再字符串中只保留了一个val值,因此需要根据结点的值val重新构建TreeNode结点对象,并且为这个结点对象的left和right进行赋值。
反序列化比序列化要难,其实代码实现是类似的,只也是使用递归,只是这时候是反向的递归,比较抽象,要逐渐理解。已知一个用!分割的字符串是某个二叉树按照先序遍历顺序序列化得到的字符串,将其反序列化建立一棵二叉树,注意,要进行反序列化必须要知道这个字符串是按照什么顺序序列化得到的,只有按照相同的遍历顺序对其进行反序列化才能恢复正确的二叉树。一般使用先序遍历顺序进行序列化和反序列化。在反序列化时,首先得到一个字符串数组strs[]表示字符串序列拆分得到的字符串数组,数组的每个元素字符串对应一个结点的值,可以是3!或者是#!,分别表示一个非空的结点或者是空结点。即要求实现的功能是:根据给定的字符串数组strs[],重建一棵二叉树并返回这棵二叉树的头结点root。
分析:对于字符串数组strs[],第1个元素是根结点,第2个元素是左结点,第3个元素可能是第2个结点的左结点或者是第1个结点的右结点,要根据第2个结点是否为null来确定,即对于strs[],里面的元素必然是按照:根结点à左结点à左结点à左结点(null)à右结点à左结点à左结点(null)à右结点的顺序来排列的,因此总是先递归地恢复建立左结点,当遇到null时,说明这条路径结束了,node结点的left为null,应该返回到node结点并开始恢复一个右结点,此时相当于一个新的重复的过程,可以把这个右结点当做root开始新的递归过程。
即要求实现一个递归方法private TreeNode deSerialize(String[] strs);对于一个(或者部分)字符串数组,恢复一棵二叉树,并返回这棵二叉树的根结点。
逐个遍历数组strs[],当遇到“#”说明这是一个空结点,在这个根结点的后面不可能建立二叉树,于是相当于建立子树工作完成,返回根结点即返回null即可;如果遇到的是非空的字符串,例如“3”,表明这是一个非空的结点,首先建立这个结点TreeNode newNode=new TreeNode(3);但是此时仅仅恢复了一个结点,还要恢复它的子树,并且是先恢复左子树,再恢复右子树。如何恢复左子树?显然要根据数组strs[]的下一个元素开始的数组部分来恢复一棵二叉树,这就是这个递归函数的功能(根据一个或者部分字符串数组来建立一棵二叉树),于是调用自身这个递归函数即可,只是此时使用的字符串向后面移动了1个元素而已。
调用完这个函数后,就要认为结点③的左子树已经恢复完毕了,于是开始恢复结点③的右子树newNode.right;恢复右子树的过程还是一样的,也是相同的逻辑(根据一个或者部分字符串数组来建立一棵二叉树),只是此时恢复的二叉树连接到的不是newNode.left上面而是newNode.right上面,当调用完这个函数后,就认为结点newNode的右子树已经恢复完成了,于是整个newNode的val有了,left、right都有了值,于是整棵二叉树就建立了,此时根据函数功能的要求,要返回这棵建立起来的二叉树的根结点,于是返回newNode即可。
常识:没有构造方法时默认有参数为空的构造函数可以不写;当写有含参的构造方法时,如果不写参数为空的构造方法,就不能再使用TreeNode newNode=new TreeNode()这种构造方法,如果要用就必须显示地定义参数为空的构造方法。
- public class Solution {
- //已知由先序遍历得到的字符串str,将其恢复为一棵二叉树,并返回根结点
- TreeNode Deserialize(String str) {
- //特殊输入
- if(str==null||str.length()<=0) return null;
- //将字符串按照","拆分为数组
- String[] strs=str.split(",");
- //调用递归方法deSerializeCore()方法来实现重建二叉树的功能,返回根结点
- TreeNode root=this.deSerializeCore(strs);
- //注意返回结果
- return root;
- }
- //注意:这里关键是要设计一个成员变量index用于在每次递归调用时能够使用不同的字符串来建立根结点
- int index=0;
- //设计一个递归方法deSerializeCore用于使用strs[]数组的后面部分元素来建立一棵二叉树,并返回根结点
- //递归方法可以有返回值或者没有返回值,不影响使用,如果有返回值要注意接收
- private TreeNode deSerializeCore(String[] strs){
- if("#".equals(strs[index])){
- //如果遇到的是#表示空节点,不再建立子树,这个结点null就是子树的根结点返回
- //千万注意,返回前要将index向下移动,之后使用的是strs[]中后面部分的元素
- index++;
- return null;
- }else{
- //如果不为空结点,则先恢复这个结点
- TreeNode newNode=new TreeNode(0);
- newNode.val=Integer.parseInt(strs[index]);
- //千万注意在递归调用之前(使用了一个元素建立结点之后),要将index向后移动1位
- index++;
- //恢复左子树
- newNode.left=this.deSerializeCore(strs);
- //恢复右子树
- newNode.right=this.deSerializeCore(strs);
- //建立二叉树完成,返回根结点
- return newNode;
- }
- }
- }