二叉树序列化与反序列化

描述

实现二叉树的序列化与反序列化。

分析

序列化二叉树的方式有很多,最经典的就是将一颗二叉树转化为字符串,这样方便存储到文件。反序列化时只需解析这个字符串,提取出每个节点的内容信息,将二叉树还原。二叉树序列化有许多应用,比如 OJ 平台在判断你提交的二叉树是否正确时,往往是将你的二叉树序列化成字符串,然后与正确答案的序列化字符串进行比较。那么如何将一颗二叉树转化成字符串呢?字符串可以认为是线性结构。以某种二叉树遍历的顺序将二叉树线性化,可以很容易建立节点与字符的映射关系。下面将分别实现先序遍历和层序遍历下的二叉树序列化及反序列化。

先序遍历

序列化

按照先序遍历的顺序将二叉树每个节点的值域转换成字符串,并添加到整个序列化字符串的后面。每两个节点之间添加 “_” 加以区分,如果节点为 null.

反序列化

将字符串以 “_” 进行分割,并用先序遍历的顺序依次创建新节点加入树中。可以用一个队列存储这些分割后子串,方便管理。

代码实现

class Serialize {
    public static String serializePreorder(Node head) {
        if (head == null) {                                     // 如果遇到空节点,则在序列化字符串中添加 "null"
            return "null_";
        }
        String res = head.element + "_";                        // 将节点的域值添加到序列化字符串后面
        res += serializePreorder(head.left);                    // 递归遍历左子树
        res += serializePreorder(head.right);                   // 递归遍历右子树
        return res;
    }
    public static Node deserializeByPreString(String str) {     
        String[] serialization = str.split("_");                // 将字符串以 "_" 分割成子串
        Queue<String> nodeQueue = new LinkedList<String>();     // 字串入队
        for (int i = 0; i < serialization.length; i++) {
            nodeQueue.offer(serialization[i]);          
        }
        return deserialPreorder(nodeQueue);                     // 调用 deserialPreorder 还原二叉树
    }
    private static Node deserialPreorder(Queue<String> nodeQueue) {
        String node = nodeQueue.poll();                         // 队首子串出队
        if (node.equals("null")) {                              // 子串值为 null,代表当前节点为空姐点,直接返回 null
            return null;
        }
        Node thisNode = new Node(Integer.valueOf(node));        // 用字串的值初始化一个节点
        thisNode.left = deserialPreorder(nodeQueue);            // 递归建立左子树
        thisNode.right = deserialPreorder(nodeQueue);           // 递归建立右子树
        return thisNode;
    }
    // 二叉树节点类
    static class Node {
        public int element;
        public Node left;
        public Node right;     
        public Node (int x) {
            this(x, null, null);
        }
        private Node(int x, Node right, Node left) {
            this.element = x;
            this.right = right;
            this.left = left;
        }
    }  
    public static void main(String[] args) {
        // 建树
        Node n1 = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        Node n4 = new Node(4);
        Node n5 = new Node(5);
        n1.left  = n2;
        n1.right = n3;
        n2.left  = n4;
        n2.right = n5;
        // 序列化字符串
        String s = serializePreorder(n1);
        System.out.println(s);
        // 反序列化
        Node head = deserializeByPreString(s);
        // 打印还原后的每个节点
        System.out.print(head.element + " ");
        System.out.print(head.left.element + " ");
        System.out.print(head.right.element + " ");
        System.out.print(head.left.left.element + " ");
        System.out.print(head.left.right.element + " ");
        System.out.println(head.right.right.element);
    }
}

out:
1_2_4_null_null_5_null_null_3_null_null_
1 2 3 4 5

层序遍历

按照层序遍历的顺序将二叉树每个节点的值域转换成字符串,并添加到整个序列化字符串的后面。每两个节点之间添加 “_” 加以区分,如果节点为 null.

反序列化

将字符串以 “_” 进行分割,并用层序遍历的顺序依次创建新节点加入树中。

代码实现

class Serialize {
    public static void main(String[] args) {
        // 建树
        Node n1 = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        Node n4 = new Node(4);
        Node n5 = new Node(5);
        n1.left = n2;
        n1.right = n3;
        n2.left = n4;
        n2.right = n5;
        String s = serializeLevelorder(n1);
        System.out.println(s);
        // 反序列化
        Node head = deserializeByLevelString(s);
        // 打印还原后的每个节点
        System.out.print(head.element + " ");
        System.out.print(head.left.element + " ");
        System.out.print(head.right.element + " ");
        System.out.print(head.left.left.element + " ");
        System.out.println(head.left.right.element + " ");
    }
    // 按层序遍历顺序序列化
    public static String serializeLevelorder(Node head) {
        if (head == null) {                                     // 如果是一颗空树,则直接返回 "null_"
            return "null_";
        } 
        Queue<Node> nodes = new LinkedList<>();                  
        String rst = "";                                        
        nodes.offer(head);                                      // 根节点加入队列
        Node node;                                              // 当前节点
        while (!nodes.isEmpty()) {                              // 队列为空则退出循环
            node = nodes.poll();                                // 队首节点出队
            if (node == null) {                                 // 如果当前节点是空节点,则在序列化字符串中添加 "null_" 
                rst += "null_";
                continue;                                       // 空节点没有指针域,直接跳过本次循环
            }   
            rst += (node.element + "_");                        // 当前节点非空,将节点值域转化成字符串添加到序列化字符串后面
            nodes.offer(node.left);                             // 当前节点左儿子入队
            nodes.offer(node.right);                            // 当前节点右儿子入队
        }    
        return rst;
    }
    // 通过层序序列化字符串反序列化
    public static Node deserializeByLevelString(String str) {      
        if (str.equals("null_"))                                        // 如果字符串为 "null_" 代表树为空,直接返回 null
            return null;            
        String[] serialization = str.split("_");                        // 将字符串以 "_" 分割成子串     
        int idx = 0;
        Queue<Node> nodes =new LinkedList<>();
        nodes.offer(new Node(Integer.valueOf(serialization[idx++])));   // 第一个子串转化成节点入队
        Node root = nodes.peek();                                       // 记住树根节点
        while (!nodes.isEmpty()) {                                      // 队列空则退出循环
            Node thisNode = nodes.poll();                               // 取出当前节点
            thisNode.left =  getNode(serialization, idx++);             // 设置当前节点的左儿子
            thisNode.right =  getNode(serialization, idx++);            // 设置当前节点的右儿子
            if (thisNode.left != null) {                                // 如果左儿子不为空,则入队
                nodes.offer(thisNode.left);
            }
            if (thisNode.right != null) {                               // 如果右儿子不为空,则入队
                nodes.offer(thisNode.right);
            }
        }
        return root;                                                    // 返回根节点
    }
    // 将字符串转化成 Node
    private static Node getNode(String[] serialization, int idx) {
        if (serialization[idx].equals("null")) {                        // 字符串为 "null" 代表是空节点,返回 null
            return null;
        }
        return new Node(Integer.valueOf(serialization[idx]));           // 返回转化后的节点
    }

out:
1_2_3_4_5_null_null_null_null_null_null_
1 2 3 4 5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值