序列化是什么?为什么需要序列化?
前言:
(1)进程想要运行,就要向操作系统申请内存空间,进程对数据的所有操作都是在内存空间中完成的。内存中有一部分数据很重要,我们希望将这些数据存储到磁盘中,就算进程被杀死、机器重启仍能找到这些数据,这时候就需要使用到序列化。
(2)以redis执行命令的流程为例,客户端(进程)想要执行一条命令,首先需要将命令封装(序列化)成redis协议指定的数据格式,随后基于socket将数据发送给redis服务(进程);redis服务收到数据后,将数据解析成具体的命令(反序列化),执行命令后,将执行结果封装成redis协议指定的数据格式(序列化)返回给客户端
序列化指将数据转化成某种可以存储或传输形式的过程。无论是将内存中的数据迁移到磁盘,还是进程间的通信都需要使用到序列化。
题目:297. 二叉树的序列化与反序列化 - 力扣(LeetCode)
题目描述:
解决方案:
题目需要我们既可以将一棵二叉树转化成字符串的形式(序列化),又可以将字符串还原成一颗二叉树(反序列化)。
(1)先序:
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
String result = serialize(root, sb);
return result;
}
private static String serialize(TreeNode root,StringBuilder sb){
//空节点用$符号填充
if(root == null)
sb.append("$,");
else{
//非空节点则添加节点值
sb.append(root.val + ",");
serialize(root.left,sb);
serialize(root.right,sb);
}
return sb.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
//获取元素是二叉树结点的字符串数组
String [] val = data.split(",");
//从字符串数组的索引0开始初始化二叉树
cnt = 0;
TreeNode root = deserialize(val);
return root;
}
//cnt:即将被创建的结点
public static int cnt;
private static TreeNode deserialize(String [] val){
String value = val[cnt++];
if (value.equals("$")){
//碰到$占位符,说明是空结点,直接返回null
return null;
}else{
//不是$,则创建结点
TreeNode node = new TreeNode(Integer.parseInt(value));
//将递归结果作为左、右子树
node.left = deserialize(val);
node.right = deserialize(val);
//返回当前结点
return node;
}
}
}
原理:
(1)序列化:以先序遍历的方式访问二叉树的每一个结点,并将结点值序列化成字符串,如果碰到空结点,就用$表示,最后会得到一个由"结点值"、"$"、","等三种字符组成的字符串。
(2)反序列化:将字符串通过split分割成一个字符串数组val,基于val数组和cnt指针以先序的顺序重建字符串。
(2)层序:配合队列逐层序列化、反序列化二叉树。
public class Codec {
public static int MAX = 10001;
//用数组模拟队列,层序遍历整棵树
public static TreeNode [] q = new TreeNode[MAX];
//head == tail时,队列的size == 0
public static int head,tail;
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
if(root != null){
//初始化队列大小
head = tail = 0;
//序列化当前结点
sb.append(root.val + ",");
//头结点入队
q[tail++] = root;
//循环结束条件:队列为空
while(head < tail){
TreeNode node = q[head++];
if(node.left != null){
//子节点不为null
//既序列化结点,又让结点入队
sb.append(node.left.val +",");
q[tail++] = node.left;
}else{
//子节点为null
//用$填充,不让结点入队
sb.append("$,");
}
if(node.right != null){
sb.append(node.right.val +",");
q[tail++] = node.right;
}else{
sb.append("$,");
}
}
}
return sb.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
//有可能是空树,空树直接返回null
if(data.equals(""))
return null;
//分割字符
String [] val = data.split(",");
//cnt变量从索引0开始遍历val字符串数组,并在遍历过程中创建结点
int cnt = 0;
//创建根节点
TreeNode root = createNode(val[cnt++]);
//重置head、tail指针
head = tail = 0;
q[tail++] = root;
while(head < tail){
TreeNode node = q[head++];
node.left = createNode(val[cnt++]);
node.right = createNode(val[cnt++]);
//左、右孩子不是空结点就入队
//因为要初始化它们的左、右孩子
if(node.left != null){
q[tail++] = node.left;
}
if(node.right != null){
q[tail++] = node.right;
}
}
return root;
}
private TreeNode createNode(String val){
//val == "$",说明是空结点,返回null
//val != "$",不是空结点,创建结点并返回
return val.equals("$") ? null : new TreeNode(Integer.parseInt(val));
}
}
层序序列化图解:
层序反序列化图解:
以上就是本文的全部内容,有什么错误的地方请评论区指正,如果你觉得本文让你有所收获,不妨点个赞!