博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
序列化与反序列化二叉树
阅读量:5337 次
发布时间:2019-06-15

本文共 5757 字,大约阅读时间需要 19 分钟。

2018-06-16 18:53:36

序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。反序列化顾名思义就是通过信息流对对象进行重建的过程。

一般来说序列化和反序列化有如下的作用:

1、以某种存储形式使自定义对象持久化;
2、将对象从一个地方传递到另一个地方。
3、使程序更具维护性。
本篇文章主要讨论的是二叉树的序列化和反序列化,分别讨论了普通二叉树和二叉搜索树的情况。
 
一、普通二叉树的序列化和反序列化
问题描述:
问题求解:
序列化和反序列化二叉树本质上其实是问如何将二叉树描述成字符串形式,如何从字符串中提取并重建二叉树。和以往建立二叉树是一样的,如果我们通过前序遍历的方式对二叉树所有节点进行表示,那么在重建二叉树的时候,就可以完全按照前序遍历的方式进行重建。问题在于,如何对数据进行表示,由于各个数的位数不同,所以一般来说会使用空格作为分隔符,同时采用"#“来表示空节点。
但是这样的序列化方式显然是比较浪费空间的,如果单纯的使用其10进制的表示,对于十几亿的数字,单纯将其转化成字符串,那么就会有11个char,占用的字节数可以会达到11个字节,这样对空间和效率都会产生不利的影响。那么如果提高空间的利用率呢?因为一个整形数本身是由4个字节来表示的,我们可以对每8位做一次转化,将之转成char,这样就可以用4个字节来表示一个整数。
事实上,下面的解法就是使用上述的思路,使用4个字节来表示一个整形数,同时可以再加上一个字节来表示是否为空节点,这样对空间的占用就会小很多,另外,由于归一化了长度,即所有数据的长度都为5个字节,那么就不需要使用空格来作为分隔符了,这样又进一步减少了空间的开销,因此,采用这种方法来序列化和反序列化二叉树是比较可取的。
public class SerializeandDeserializeTree {    // Encodes a tree to a single string.    public String serialize(TreeNode root) {        StringBuffer sb = new StringBuffer();        preOrder(root, sb);        return sb.toString();    }    private void preOrder(TreeNode root, StringBuffer stringBuffer) {        if (root == null) {            stringBuffer.append("00000");        }        else {            byte[] bytes = intToByte(root.val);            stringBuffer.append('1').append((char) bytes[0]).append((char) bytes[1]).append((char) bytes[2]).append((char) bytes[3]);            preOrder(root.left, stringBuffer);            preOrder(root.right, stringBuffer);        }    }    private byte[] intToByte(int val) {        return new byte[]{                (byte) (val >> 24),                (byte) (val >> 16),                (byte) (val >> 8),                (byte) val        };    }    // Decodes your encoded data to tree.    public TreeNode deserialize(String data) {        return helper(data.toCharArray(), new int[]{0});    }    private TreeNode helper(char[] data, int[] pos) {        if (pos[0] >= data.length) return null;        if (data[pos[0]] == '0') {            pos[0] += 5;            return null;        }        int val =                (data[pos[0] + 1]) << 24 & 0xff000000 |                (data[pos[0] + 2]) << 16 & 0x00ff0000 |                (data[pos[0] + 3]) << 8 & 0x0000ff00 |                (data[pos[0] + 4]) << 0 & 0x000000ff;        pos[0] += 5;        TreeNode root = new TreeNode(val);        root.left = helper(data, pos);        root.right = helper(data, pos);        return root;    }}

精简版:

public class Codec {    // Encodes a tree to a single string.    public String serialize(TreeNode root) {        StringBuffer sb = new StringBuffer();        preOrder(root, sb);        return sb.toString();    }    private void preOrder(TreeNode root, StringBuffer sb) {        if (root == null) sb.append("00000");        else {            int val = root.val;            sb.append('1').append((char) (val >> 24)).append((char) ((val >> 16) & (0x00ff))).append((char) ((val >> 8) & (0x0000ff))).append((char) (val & (0x000000ff)));            preOrder(root.left, sb);            preOrder(root.right, sb);        }    }        // Decodes your encoded data to tree.    public TreeNode deserialize(String data) {        return helper(data.toCharArray(), new int[1]);    }    private TreeNode helper(char[] chs, int[] pos) {        if (pos[0] >= chs.length) return null;        if (chs[pos[0]] == '0') {            pos[0] += 5;            return null;        }        int val = chs[pos[0] + 1] << 24 | chs[pos[0] + 2] << 16 | chs[pos[0] + 3] << 8 | chs[pos[0] + 4];        TreeNode root = new TreeNode(val);        pos[0] += 5;        root.left = helper(chs, pos);        root.right = helper(chs, pos);        return root;    }}

 

二、 二叉搜索树的序列化和反序列化

问题描述:

问题求解:

二叉搜索树是二叉树的特例,当然是可以使用上面的思路直接AC的,但是本题中特别要求了,序列化的字符串要尽可能的紧凑,考虑到二叉搜索树的性质,我们可以进一步的简化序列化的长度。正如上面提到的,对于整形数使用4个字节表示可以大大简化存储数量,这个是毋庸置疑的,只是在二叉搜索树中,我们不再需要额外存储空节点信息,因为对于二叉搜索树来说,其性质是左子树所有节点都要比根节点小,右子树所有节点都要比根节点大,因此我们可以利用这个性质来判断当前流数据是否为此根节点的子树。
综上所述,我们需要维护一下每个根节点左右子树的大小范围以判断当前的流数据是否应该挂在当前根节点下,如果不在范围内,直接返回空值即可。因而,对于整个前序遍历的序列化字符串来说,我们只需要4个字节来表述整形数即可。
public class SerializeandDeserializeBST {    // Encodes a tree to a single string.    public String serialize(TreeNode root) {        StringBuffer sb = new StringBuffer();        preOrder(root, sb);        return sb.toString();    }    byte[] intToByte(int val) {        return new byte[]{                (byte)(val >> 24),                (byte)(val >> 16),                (byte)(val >> 8),                (byte)val        };    }    void preOrder(TreeNode root, StringBuffer sb) {        if (root != null) {            byte[] tmp = intToByte(root.val);            sb.append((char) tmp[0]).append((char) tmp[1]).append((char) tmp[2]).append((char) tmp[3]);            preOrder(root.left, sb);            preOrder(root.right, sb);        }    }    // Decodes your encoded data to tree.    public TreeNode deserialize(String data) {        return helper(data.toCharArray(), new int[]{0}, Integer.MIN_VALUE, Integer.MAX_VALUE);    }    private TreeNode helper(char[] data, int[] pos, int low, int high) {        if(pos[0] >= data.length) return null;        int val =                data[pos[0] + 0] << 24 & (0xff000000) |                data[pos[0] + 1] << 16 & (0x00ff0000) |                data[pos[0] + 2] << 8 & (0x0000ff00) |                data[pos[0] + 3] << 0 & (0x000000ff);        if(val < low || val > high) return null;        TreeNode root = new TreeNode(val);        pos[0] += 4;        root.left = helper(data, pos, low, val);        root.right = helper(data, pos, val, high);        return root;    }    int byteToInt(byte[] bytes) {        return                bytes[0] & (0xff000000) |                bytes[1] & (0x00ff0000) |                bytes[2] & (0x0000ff00) |                bytes[3] & (0x000000ff);    }}

 

 
 
 
 
 
 
 

转载于:https://www.cnblogs.com/TIMHY/p/9191122.html

你可能感兴趣的文章
core--线程池
查看>>
redux-effect
查看>>
Swift和OC混编
查看>>
Android轻量级的开源缓存框架ASimpleCache
查看>>
他山之石:加载图片的一个小问题
查看>>
shell - 常识
查看>>
mssql sqlserver 使用sql脚本 清空所有数据库表数据的方法分享
查看>>
分层图最短路【bzoj2763】: [JLOI2011]飞行路线
查看>>
linux下编译复数类型引发的错误:expected unqualified-id before '(' token
查看>>
codeforces 1041A Heist
查看>>
字典常用方法
查看>>
Spring Cloud Stream消费失败后的处理策略(三):使用DLQ队列(RabbitMQ)
查看>>
bzoj1048 [HAOI2007]分割矩阵
查看>>
Java中的编码
查看>>
PKUWC2018 5/6
查看>>
As-If-Serial 理解
查看>>
洛谷P1005 矩阵取数游戏
查看>>
在Silverlight中使用HierarchicalDataTemplate为TreeView实现递归树状结构
查看>>
无线通信基础(一):无线网络演进
查看>>
如何在工作中快速成长?阿里资深架构师给工程师的10个简单技巧
查看>>