二叉树的序列化和反序列化

1.二叉树的按层遍历

这里有一个二叉树,我们按层遍历,应该输出 a b c d e f

那么,我们可以怎么实现呢?

我们可以通过队列来实现。

当然实现代码也很简单,在此奉上

public void levelBinary(Node head){
        if (head == null){
            return;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        while (!queue.isEmpty()){
            Node poll = queue.poll();
            System.out.println(poll.value);
            if (poll.left != null){
                queue.add(poll.left);
            }
            if (poll.right != null){
                queue.add(poll.right);
            }
        }
    }

该代码只能按照层序将二叉树输出,并不能知道该节点在哪一层,当然我们可以对该代码进行改进,使得我们可以知道该节点在哪一层。

2.二叉树的序列化和反序列化

2.1什么是序列化和反序列化

2.1.1序列化

把内存中的二叉树变成文件的形式,最好把二叉树转成一个字符串,该字符串可以代表一个唯一的结构,并且可以还原出唯一的树来,将二叉树变为字符串的结构,叫做序列化

2.1.2反序列化

我们把转换成的字符串,还原成唯一的树结构,叫做反序列化

2.2为什么需要序列化和反序列化

我们知道,二叉树是内存中的结构,没有不挂的服务,假如机器要停机,我们想人为控制把树变成文件的形式,就需要序列化,当我们重启的时候,或者移植到其他服务的时候,我们需要把文件还原成唯一的二叉树,就需要反序列化

2.3 怎么做?

我们可以通过二叉树的先序遍历或者按层遍历来实现。

比如说这么一个二叉树

首先我们认为null不可忽略,null相当于一个占位符,用来保存结构的,我们可以将二叉树想象成这个样子

然后我们进行先序遍历,遍历到谁,就直接序列化了,null不可忽略,用于保存结构,该例子中用#代替,其实可以用任意字符,只要我们在还原时用相同的字符还原即可,该二叉树序列化后为

"1,1,#,1,#,#,#,"

我们如果使用先序遍历进行序列化,那么就应该用先序遍历进行反序列化

先序遍历序列化代码

private static Queue<String> preSerial(Node head){
        Queue<String> queue = new LinkedList<>();
        pres(queue,head);
        return queue;
    }
    public static void pres(Queue<String> queue,Node head){
        if (head == null){
            queue.add(null);
        }else {
            queue.add(String.valueOf(head.value));
            pres(queue,head.left);
            pres(queue,head.right);
        }
    }

先序遍历反序列化代码

public static Node buildByQueue(Queue<String> queue){
        if (queue == null || queue.size() == 0){
            return null;
        }
        return preb(queue);
    }
    public static Node preb(Queue<String> queue){
        String headStr = queue.poll();
        if (headStr == null){
            return null;
        }
        Node headNode = new Node(Integer.valueOf(headStr));
        headNode.left = preb(queue);
        headNode.right = preb(queue);
        return headNode;
    }

那既然有先序序列化,是由也会有中序序列化和后续序列化呢

后序序列化是有的,我们可以根据先序的改,代码大差不差,但是中序序列化是没有的,因为即使null不可忽略,我们根据中序排序,也没办法还原出唯一的二叉树。譬如

/**
     *          2
     *        /
     *       1
     *       和
     *       1
     *        \
     *        2
     *  补足空位的中序遍历结果都是 { null,1,null,2,null }
     */

 所以没办法根据中序遍历进行序列化和反序列化

按层进行序列化

可以根据上文提到的二叉树按层遍历进行改造,改成按层进行序列化,其实就是在按层遍历的基础上添加了一些东西。

public static Queue<String> levelSerial(Node head){
        Queue<String> queue = new LinkedList<>();
        if (head == null){
            queue.add(null);
        }else {
            queue.add(String.valueOf(head.value));
            Queue<Node> levelQueue = new LinkedList<>();
            levelQueue.add(head);
            while (!levelQueue.isEmpty()){
                head = levelQueue.poll();
                if (head.left != null){
                    levelQueue.add(head.left);
                    queue.add(String.valueOf(head.left.value));
                }else {
                    queue.add(null);
                }
                if (head.right != null){
                    levelQueue.add(head.right);
                    queue.add(String.valueOf(head.right.value));
                }else {
                    queue.add(null);
                }
            }
        }
        return queue;
    }

按层反序列化

public static Node buildByLevelQueue(Queue<String> queue){
        if (queue == null || queue.size() == 0){
            return null;
        }
        Node head = generateNode(queue.poll());
        Queue<Node> nodeQueue = new LinkedList<>();
        if (head != null){
            nodeQueue.add(head);
        }
        Node node = null;
        while (!nodeQueue.isEmpty()){
            node = nodeQueue.poll();
            node.left = generateNode(queue.poll());
            node.right = generateNode(queue.poll());
            if (node.left != null){
                nodeQueue.add(node.left);
            }
            if (node.right != null){
                nodeQueue.add(node.right);
            }
        }
        return head;
    }

    private static Node generateNode(String poll) {
        if (poll == null){
            return null;
        }
        return new Node(Integer.valueOf(poll));
    }

3.在1中的按层遍历中,我们只有一个树按层遍历的一个结果,并不能知道哪一层有多少个值,假如说我如果想要获取到树的最大宽度,我们应该怎么做呢?

我们可以在按层遍历的过程中,新增加几个变量来实现。

获取二叉树的最大宽度代码:

public int getTreeMaxWidth(Node head){
        if (head == null){
            return 0;
        }
        Queue<Node> queue = new LinkedList<>();
        Node curEnd = head;
        Node nextEnd = null;
        queue.add(head);
        int max = 0;
        int count = 0;      //当前节点数
        while (!queue.isEmpty()){
            Node poll = queue.poll();
            count ++;
            if (poll.left != null){
                queue.add(poll.left);
                nextEnd = poll.left;
            }
            if (poll.right != null){
                queue.add(poll.right);
                nextEnd = poll.right;
            }
            if (curEnd == poll){
                if (count > max){
                    max = count;
                }
                count = 0;
                curEnd = nextEnd;
            }
        }
        return max;
    }

curEnd用来保存当前最后一个节点,nextEnd用来保存下一层的最后一个节点,max表示最大的宽度,count表示当前的节点数,在队列中,我们弹一个数字,count++,当到当前节点是该层最后一个节点时,count和max比较,count 清零,把下一层最后一个节点nextEnd的值给当层的最后一个节点curEnd,开始下一层的遍历。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值