目录
一.序列化和反序列化
1.什么是序列化和反序列化
二.前序遍历
1.序列化
1.问题分析
2.代码实现
2.反序列化
1.问题分析
2.代码实现
三.后序遍历
1.序列化
1.思路分析
2.代码实现
2.反序列化
1.思路分析
2.代码实现
四.中序遍历
1.序列化
1.思路分析
2.代码实现
2.反序列化
1.思路分析
5.层序遍历
1.序列化
1.思路分析
2.代码实现
2.反序列化
1.思路分析
2.代码实现
序列化(Serialization)是将对象转化为字节序列的过程,方便数据在网络传输和存储过程中的传递。在序列化过程中,对象的状态信息被转换为可以存储或传输的格式(如字节数组、XML、JSON等),以便在需要时能够将其反序列化为原始对象。序列化可以用于在不同的系统之间传输对象,或者将对象存储到磁盘上等。
反序列化(Deserialization)则是将序列化后的字节流还原成对象的过程,即恢复原始的对象状态信息。在反序列化过程中,可以根据序列化时使用的格式,将字节流转换成原始对象,使得原本序列化的对象状态得以恢复。
序列化和反序列化可以使得对象的状态信息在不同的应用程序或系统之间传输或存储变得更加容易和可靠。
我们这里的序列化就是将树对象转化为字符串,反序列化就是将字符串重新转换为树对象
我们先根据前序遍历将树转换为一个字符串,其实很简单,就是一个前序遍历,加上字符串的拼接操作,例如现在题目有如下要求,根据前序遍历将树转换为字符串,空节点用'#'代替,相邻结点用','分割开
例如上图的二叉树,我们序列化成为字符串应该为{2,1,#,6,#,#,3,#,#}
我们代码其实很好实现,这其实就是前序遍历,到空节点的是后拼接#即可
如果对前序,中序,后序,层序遍历不明白的,推荐看这篇文章的讲解:树的遍历方式(前中后,层序遍历,递归,迭代,Morris遍历)-----直接查询代码_允歆辰丶的博客-CSDN博客
//前序遍历序列化
public String preOrderSerialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
preOrderSerialize(root, sb);
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
public void preOrderSerialize(TreeNode root, StringBuilder sb) {
if (root == null) {
sb.append("#" + ",");
return;
}
sb.append(root.val + ",");
preOrderSerialize(root.left, sb);
preOrderSerialize(root.right, sb);
}
我们如何根据字符串反序列化这棵树呢?其实递归的思路还是参考前序遍历.对于这个字符串data,每个结点都是","进行隔开的,而前序遍历的第一个元素就是根结点(关键也在于找到每一颗子树的根结点),因此我们字符串从前到后遍历,这样一层层的构建结点,然后递归后来的时候进行连接,整个二叉树就构建成功了,看下面这个图会更容易的理解为什么这样进行递归
//前序遍历反序列化
public TreeNode preOrderDeserialize(String data) {
String[] split = data.split(",");
return preOrderDeserialize(split);
}
int index = 0;
public TreeNode preOrderDeserialize(String[] nodes) {
if (index >= nodes.length) {
return null;
}
String node = nodes[index++];
if (node.equals("#")) {
return null;
}
//根
TreeNode root = new TreeNode(Integer.parseInt(node));
//左
root.left = preOrderDeserialize(nodes);
//右
root.right = preOrderDeserialize(nodes);
return root;
}
还是这颗二叉树,根据后序遍历的顺序,也是很容易就可以写出代码
这后序遍历序列化的字符串应该为:{#,#,#,6,1,#,#,3,2};
//后序遍历序列化
public String postOrderSerialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
postOrderSerialize(root, sb);
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
public void postOrderSerialize(TreeNode root, StringBuilder sb) {
if (root == null) {
sb.append("#" + ",");
return;
}
postOrderSerialize(root.left, sb);
postOrderSerialize(root.right, sb);
sb.append(root.val + ",");
}
后序遍历的反序列化和前序遍历的反序列化还是有一定的不同的,但大致的思路是一样的,就是寻找根节点的位置,然后递归一步步的建立二叉树,根据后序遍历的特点,我们得知这棵树的根结点在字符串最后的位置,然后将index前移,这个时候我们可以照抄前序遍历反序列化的代码了吗?当然不是,我们根据下面这个图可以很清楚的看到,root结点的前边是右子树的结点,因此我们应该先建立右子树,然后再建立左子树.
//后序遍历反序列化
public TreeNode postOrderDeserialize(String data) {
String[] split = data.split(",");
index2 = split.length - 1;
return postOrderDeserialize(split);
}
int index2;
public TreeNode postOrderDeserialize(String[] nodes) {
if (index2 < 0) {
return null;
}
String node = nodes[index2--];
if (node.equals("#")) {
return null;
}
//根
TreeNode root = new TreeNode(Integer.parseInt(node));
//左
root.right = postOrderDeserialize(nodes);
//右
root.left = postOrderDeserialize(nodes);
return root;
}
和上面两题的思路一样,根据中序遍历的顺序,完成中序遍历的序列化
还是上面的树,中序遍历的序列化答案应该是为:{#,1,#,6,#,2,#,3,#};
//中序遍历序列化
public String infixOrderSerialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
infixOrderSerialize(root, sb);
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
public void infixOrderSerialize(TreeNode root, StringBuilder sb) {
if (root == null) {
sb.append("#" + ",");
return;
}
infixOrderSerialize(root.left, sb);
sb.append(root.val + ",");
infixOrderSerialize(root.right, sb);
}
中序遍历无法进行反序列化,我们在这里可以思考一下原因,在前序遍历和后序遍历的反序列化中,我们最主要的步骤就是找根结点,而在中序遍历的字符串中,我们无法确定根结点的位置,因此我们不能进行反序列化
层序遍历其实就是借助队列来进行序列化,和层序遍历一样,一层一层的,只不过遇到非空节点,我们需要添加到队列中,因为在进行序列化的时候,空节点默认转换为"#",如果出队列的元素为null的时候,我们添加这个元素,并且进行continue操作.
层序遍历的序列化结果为:{2,1,3,#,6,#,#,#,#};
public String levelOrderSerialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
LinkedList queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode pop = queue.pop();
if (pop == null) {
sb.append("#" + ",");
continue;
}
sb.append(pop.val + ",");
queue.offer(pop.left);
queue.offer(pop.right);
}
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
反序列化的思路其实和序列化的思路还是很想的,每一次找到根结点,然后将左右节点入队列,依次进行下去就可以了.
public TreeNode levelOrderDeserialize(String data) {
String[] nodes = data.split(",");
if (nodes[0].equals("#")) {
return null;
}
int index = 1;
TreeNode root = new TreeNode(Integer.parseInt(nodes[0]));
LinkedList queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode parent = queue.pop();
if (nodes[index].equals("#")) {
parent.left = null;
} else {
parent.left = new TreeNode(Integer.parseInt(nodes[index]));
queue.offer(parent.left);
}
index++;
if (nodes[index].equals("#")) {
parent.right = null;
} else {
parent.right = new TreeNode(Integer.parseInt(nodes[index]));
queue.offer(parent.right);
}
index++;
}
return root;
}