二叉树的序列化和反序列化(建树)

二叉树的序列化和反序列化(建树)

提示:搞清楚建树的步骤
递归序的本质要搞清楚,三次见面:
(1)二叉树,二叉树的归先序遍历,中序遍历,后序遍历,递归和非递归实现


题目

请你将二叉树序列化,并根据序列化的队列结果,反序列化,把二叉树还原
下面这个树,序列化之后,是1 2 null null 3 null null
在这里插入图片描述
根据队列:1 2 null null 3 null null
反序列化,把树恢复建出来
在这里插入图片描述

本题所用节点与树:

 public static class Node{
        public int value;
        public Node left;
        public Node right;
        public Node(int v){
            value = v;
        }
    }

    //构造一颗树,今后方便使用
    public static Node generateBinaryTree(){
        //树长啥样呢
        //          1
        //        2    3
        Node head = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        head.left = n2;
        head.right = n3;

        return head;
    }

DFS查看二叉树

//打印树的DFS
    //先序遍历打印
    public static void prePrint(Node head){
        if(head == null) return;//不管是头还是叶节点,这就是递归的终止条件
        //先打印头,再打印左子树,再打印右子树
        System.out.print(head.value +" ");
        prePrint(head.left);
        prePrint(head.right);
    }
    public static void inPrint(Node head){
        if(head == null) return;
        //中序遍历,先打印左边,再打印头,再打印右边
        inPrint(head.left);
        System.out.print(head.value +" ");
        inPrint(head.right);
    }
    public static void postPrint(Node head){
        if(head == null) return;
        //后序遍历打印,先打印左边,再打印右边,最后打印头
        postPrint(head.left);
        postPrint(head.right);
        System.out.print(head.value +" ");
    }

一、为什么要序列化二叉树

因为,程序中,往往有的进程,需要先暂停,保存,方便停电之后又从原来执行过的地方恢复继续执行
感觉是想浏览器,你原来浏览了哪些地方,关闭电脑前
需要将进程(树)保存好,那就需要序列化,然后存入文件
下次要用的时候,咱们再将其反序列化,恢复出来继续使用。
在这里插入图片描述


二、DFS方式二叉树如何序列化?如何反序列化?

DFS序列化二叉树

序列化有几种方式:先序序列化,中序序列化,后序序列化
无非就是在DFS遍历过程中,遇到null也要保存
序列化比遍历打印就多出null来
这样才能保证回头我们可以恢复树

有了递归序:
(1)二叉树,二叉树的归先序遍历,中序遍历,后序遍历,递归和非递归实现
第一次见面就保存,叫先序序列化
第二次见面就保存,叫先中序序列化
第三次见面就保存,叫后序序序列化

【反正遍历过程中遇到null照样继续加】
不放用先序序列化做例子:
(1)第一次见面1点,加入队列ans;
(2)遍历1点的左子,第一次见面2点,加入ans;
(3)遍历2点的左子,null,加入ans;
(4)遍历2点的右子,null,加入ans;
(5)遍历点1的右子3点,第一次见面3点,加入ans;左右子null均加入
完成序列化。
在这里插入图片描述
三种序列化方式手撕代码:

先序序列化

//先序序列化
    public static Queue<String> preSerial(Node head){
        if (head == null) return null;

        Queue<String> queue = new LinkedList<>();//内部是字符串,我们回头可以建
        serialPre(head, queue);

        return queue;
    }
    public static void serialPre(Node head, Queue<String> queue){
        if (head == null) queue.add(null);
        else {
            //第一次见面就玩--先序
            queue.add(String.valueOf(head.value));

            //继续遍历左右子
            serialPre(head.left, queue);
            serialPre(head.right, queue);
        }
    }

    public static void test(){
        Node head = generateBinaryTree();
        //先序打印看看
        System.out.println("先序打印:");
        prePrint(head);
        System.out.println();
        System.out.println("先序序列化");
        Queue<String> ans = preSerial(head);
        for(String s:ans) System.out.print(s +" ");
    }


    public static void main(String[] args) {
        test();
    }
先序打印:
1 2 3 
先序序列化
1 2 null null 3 null null

中序序列化

//中序序列化
    public static Queue<String> inSerial(Node head){
        if (head == null) return null;

        Queue<String> queue = new LinkedList<>();
        serialIn(head, queue);

        return queue;
    }
    public static void serialIn(Node head, Queue<String> queue){
        //沿途遍历见到的元素加入queue,DFS的骚操作
        if (head == null) queue.add(null);
        else {
            //第二次见面才能加哦,所以要先去看左树
            serialIn(head.left, queue);
            queue.add(String.valueOf(head.value));//2次见面玩叫中序
            serialIn(head.right, queue);
        }
    }

后序序列化

//后序序列化
    public static Queue<String> postSerial(Node head){
        if (head == null) return null;

        Queue<String> queue = new LinkedList<>();
        serialPost(head, queue);

        return queue;
    }
    public static void serialPost(Node head, Queue<String> queue){
        //沿途遍历见到的元素加入queue,DFS的骚操作
        if (head == null) queue.add(null);
        else {
            //第三次见面才能加哦,所以要先去看左树和右树
            serialPost(head.left, queue);
            serialPost(head.right, queue);
            queue.add(String.valueOf(head.value));//3次见面玩叫后序
        }
    }

测试一把:

public static void test(){
        Node head = generateBinaryTree();
        //先序打印看看
        System.out.println("先序打印:");
        prePrint(head);
        System.out.println();
        System.out.println("先序序列化");
        Queue<String> ans = preSerial(head);
        for(String s:ans) System.out.print(s +" ");
        System.out.println("\n------------------");

        //中序打印看看
        System.out.println("中序打印:");
        inPrint(head);
        System.out.println();
        System.out.println("中序序列化");
        Queue<String> ans2 = inSerial(head);
        for(String s:ans2) System.out.print(s +" ");
        System.out.println("\n------------------");

        //后序打印看看
        System.out.println("后序打印:");
        postPrint(head);
        System.out.println();
        System.out.println("后序序列化");
        Queue<String> ans3 = postSerial(head);
        for(String s:ans3) System.out.print(s +" ");
        System.out.println("\n------------------");
    }


    public static void main(String[] args) {
        test();
    }

看结果:

先序打印:
1 2 3 
先序序列化
1 2 null null 3 null null 
------------------
中序打印:
2 1 3 
中序序列化
null 2 null 1 null 3 null 
------------------
后序打印:
2 3 1 
后序序列化
null null 2 null null 3 1 
------------------

根据一个DFS序列化的队列结果,如何恢复二叉树?

你是怎么序列化的就怎么反序列化,
无非就是消耗结果中的元素,建头节点,然后建左树,建右树
然后将左右树挂在头节点上,返回头节点。
在这里插入图片描述

建节点可能有null,可能是节点:

//建一个节点,可能是null,可能有value
    public static Node creatNode(String value){
        return value == null ? null : new Node(Integer.valueOf(value));
    }

反序列化这个过程需要:也是DFS的过程
如果是:先建头节点,然后建左树,然后建右树——先序反序列化过程。
如果是:先建左树,再建头节点,再建右树——中序反序列化过程。
如果是:先建左树,再建右树,再建头节点——后序反序列化过程。

下面我们分别实现:

如果是:先建头节点,然后建左树,然后建右树——先序反序列化过程。

//如果是:先建头节点,然后建左树,然后建右树——先序反序列化过程。
    public static Node buildPreFromSerial(Queue<String> ans){
        if (ans == null || ans.size() == 0) return null;//没有点

        //DFS建树:依次让那些点去建树,挂
        return bulidPre(ans);
    }
    public static Node bulidPre(Queue<String> ans){
        String value = ans.poll();//弹
        if (value == null) return null;

        //建点的顺序,决定了你是先序反序列化,中序反序列化,还是后序反序列化
        Node head = creatNode(value);//先建头节点--先序反序列化
        head.left = bulidPre(ans);//建左树
        head.right = bulidPre(ans);//建右树

        return head;//从x开始建好左右子,返回x就是答案。
    }
public static void test2(){
        Node head = generateBinaryTree();

        System.out.println("先序打印结果:");
        prePrint(head);
        System.out.println("\n先序序列化:");
        Queue<String> ansPre = preSerial(head);
        for(String s:ansPre) System.out.print(s +" ");
        System.out.println("\n先序反序列化建树");
        Node headPre = buildPreFromSerial(ansPre);
        System.out.println("先序打印反序列化后的结果:");
        prePrint(headPre);
        System.out.println("\n-------------");

    }

    public static void main(String[] args) {
//        test();
        test2();
    }

目前只成功了这先序序列化的反序列化代码:

先序打印结果:
1 2 3 
先序序列化:
1 2 null null 3 null null 
先序反序列化建树
先序打印反序列化后的结果:
1 2 3 
-------------

如果是:先建左树,再建头节点,再建右树——中序反序列化过程。

头直接就是null,咱要怎么实现中序建树呢
没想通

如果是:先建左树,再建右树,再建头节点——后序反序列化过程。

这个暂时不会


三、BFS方式如何序列化二叉树?如何反序列化?

BFS方式如何序列化二叉树

有上上面DFS序列化的经验,不就是序列化的时候,要把null加入吗?
反序列化的时候,也会是同样的递归代码,消耗序列化结果,建头节点,挂左右子,即可

BFS宏观调度,左右子谁遇到空就加null

//BFS序列化
    public static Queue<String> bfsSerial(Node head){
        if (head == null) return null;
        
        Queue<String> ans = new LinkedList<>();
        Queue<Node> queue = new LinkedList<>();//bfs的
        
        queue.add(head);
        ans.add(String.valueOf(head.value));//头入结果
        
        while (!queue.isEmpty()){
            Node cur = queue.poll();
            if (cur.left != null){
                queue.add(cur.left);
                ans.add(String.valueOf(cur.left.value));//加结果
            }else ans.add(null);//这里要注意,null也要加
            
            if (cur.right != null){
                queue.add(cur.right);
                ans.add(String.valueOf(cur.right.value));
            }else ans.add(null);
        }
        
        return ans;
    }
    
    //BFS反序列化
    
    public static void test3(){
        Node head = generateBinaryTree();
        System.out.println("bsf打印:");
        bfsBinaryTreePrint(head);
        System.out.println("\n序列化后打印:");
        Queue<String> ans = bfsSerial(head);
        for(String s:ans) System.out.print(s +" ");
    }
    
    public static void main(String[] args) {
//        test();
//        test2();
        test3();
    }

BFS方式序列化二叉树的结果,如何反序列化?

怎么通过BFS序列化,就怎么消耗结果又造回来
建头,左树,右树,挂头完成

//BFS反序列化
    public static Node bfsBackBuildTree(Queue<String> ans){
        if (ans == null || ans.size() == 0) return null;

        //BFS宏观控制
        LinkedList<Node> queue = new LinkedList<>();
        Node head = creatNode(ans.poll());
        queue.add(head);

        while (!queue.isEmpty()){
            Node cur = queue.poll();
            //消耗2个,建左右树
            cur.left = creatNode(ans.poll());
            cur.right = creatNode(ans.poll());

            //按层加入左右子--长队bfs
            if (cur.left != null) queue.add(cur.left);
            if (cur.right != null) queue.add(cur.right);
        }

        return head;
    }
    
    public static void test3(){
        Node head = generateBinaryTree();
        System.out.println("bsf打印:");
        bfsBinaryTreePrint(head);
        System.out.println("\n序列化后打印:");
        Queue<String> ans = bfsSerial(head);
        for(String s:ans) System.out.print(s +" ");

        System.out.println("\nBFS反序列化后打印:");
        Node headBFS = bfsBackBuildTree(ans);
        bfsBinaryTreePrint(headBFS);
    }
    
    public static void main(String[] args) {
//        test();
//        test2();
        test3();
    }

总结

提示:重要经验:

1)涉及图或者二叉树的BFS的话,先把BFS代码写出来,咱再改为自己的代码,这个BFS代码尤其重要。
2)二叉树的序列化和反序列化,包括DFS序列化和BFS序列化方式,都要加null,你怎么序列化,就同样的代码反序列化,无非就是消耗元素建新节点。
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值