树的序列化与反序列化java - Kaiqisan

2023-11-02

大家好,都吃晚饭了吗?我是Kaiqisan,是一个已经走出社恐的一般生徒

为什么引入这个概念

在计算机中,如果我们如果想要可视化一棵树,那会是非常困难的工作,所以,我们就想到了一种最简单的方法来表示一棵树,而且只使用字符串,也可以区分每一颗树的不同

现在有两棵树

		1
	   /
	  2
	 /
	3
	1
	 \
	  2
	    \
	  	 3

如果我们使用同样的遍历方法,结果都是123,如果一个二叉树的结构未知,我们只是使用遍历的方法,我们将永远无法获取一棵树的结构。
所以我们在遍历的时候使用序列化,也就是使用分隔符来代替空节点填充进去,那么现在的树结构是这样的
那么我们重新遍历一遍(使用先序遍历)结果分别为 123#### 1#2#3##,为防止歧义,我们也可以在每一个元素都使用分隔符(1 _ 2 _ 3_ # _ # _ # _ # ),这样就可以找到两个数的区别了,这也是最简单的树的表达方法

		1
	   / \
	  2	  #
	 / \
	3   #
   / \
  #   #
	1
   / \
  #    2
	  / \
	 #	 3
	    / \
	   #   #
/* 序列化参考代码 */
	public static void main(String[] args) {
        Tree head = new Tree(1);
        Tree a2 = new Tree(2);
        Tree a3 = new Tree(3);
        Tree a4 = new Tree(4);
        Tree a5 = new Tree(5);
        Tree a6 = new Tree(6);
        Tree a7 = new Tree(7);
        Tree a8 = new Tree(8);

		// 链接树
        head.left = a2;
        head.right = a3;
        a2.left = a4;
        a2.right = a5;
        a3.left = a6;
        a3.right = a7;
        a6.right = a8;

        // 序列化
        doOrderify(head, head);
    }

	static void doOrderify(Tree head, Tree realHead) {
        if (head == null) {
            System.out.print("_" + "#");
            return;
        }
        if (head == realHead) {
        	// 对输出的工整性进行调整,第一个元素不输出下划线
            System.out.print(head.val);
        } else {
            System.out.print("_" + head.val);
        }
        doOrderify(head.left, null);
        doOrderify(head.right, null);
    }

正如编码一般,我们还需要反编码,这个序列化也需要反序列化,重新把它转化为一棵树
以上面的 1#2#3## 为例,我们如何把它转化为一颗真正的树呢?

还是遍历把,这里提供两种方法

方法1

投机取巧法(非递归)

先把第一个元素入栈(因为一开始序列化使用先序遍历,所以这里的第一个元素必定不是#)

然后查看这个栈中第一个元素是否有左节点,如果有,就判断序列化字符串的下一个元素是否为#,如果不为#,就新建一个节点,连接,作为自己的左孩子,指针转向这个左孩子,如果遍历元素为#,就跳过。如果头结点的左孩子已经有元素了,就重复上面的操作,只不过把都换成就行了。

在遍历之前,先判断当前遍历的序列元素和它下一个元素是否都为#,如果都是#,就代表当前的指针指向的节点无左孩子,也无右孩子(因为null节点无孩子指针,不可能有 null.left -> null,所以只可能是node.left = null node.right = null),此时,出栈一个元素,指针指向当前节点的父节点,回到上一层

	static Tree startBuild(String[] arr) {
        Stack<Tree> temp = new Stack<>();
        Tree head = new Tree(arr[0]);
        temp.push(head);
        Tree res = head; // 临时变量
        for (int i = 1; i < arr.length - 1; i++) {
            if (arr[i].equals("#") && arr[i + 1].equals("#")) {
                while (head.left != null && head.right != null) {
                    temp.pop();
                    head = temp.peek();
                }
                continue;
            }
            if (temp.peek().left == null) {
                if (arr[i].equals("#")) {
                    continue;
                }
                temp.peek().left = new Tree(arr[i]);
                head = temp.peek().left;
                temp.push(head);
            } else if (temp.peek().right == null) {
                if (arr[i].equals("#")) {
                    continue;
                }
                temp.peek().right = new Tree(arr[i]);
                head = temp.peek().right;
                temp.push(head);
            }
        }
        return res;
    }

方法2

频繁挪动法(非递归)

不使用栈,树节点有额外指针parent会指向父亲节点。

先利用序列的第一个元素来生成根节点,也让指针指向这个根节点。

每次遍历序列,判断当前的指针指向的节点的左右孩子是否为空,(先判断左孩子,再判断右孩子),如果有不为空的,就开始生成新节点,然后焊接成当前节点的孩子,然后指针指向这个孩子,如果左右节点都有了,那就往上钻,回到父亲节点,如果父亲节点也是左右节点都有了,那就接着往往上钻,直到找到有空档(子节点没有满)的父节点,然后继续前面的操作。

	static Tree startBuild2(String[] arr) {
        Stack<Tree> temp = new Stack<>();
        Tree head = new Tree(arr[0]);
        temp.push(head);
        Tree res = head; // 临时变量
        for (int i = 1; i < arr.length - 1; i++) {
            if (arr[i].equals("#") && arr[i + 1].equals("#")) {
                while (head.left != null && head.right != null) {
                    temp.pop();
                    head = temp.peek();
                }
                continue;
            }
            if (temp.peek().left == null) {
                if (arr[i].equals("#")) {
                    continue;
                }
                temp.peek().left = new Tree(arr[i]);
                head = temp.peek().left;
                temp.push(head);
            } else if (temp.peek().right == null) {
                if (arr[i].equals("#")) {
                    continue;
                }
                temp.peek().right = new Tree(arr[i]);
                head = temp.peek().right;
                temp.push(head);
            }
        }
        return res;
    }

方法3

递归法

很简单就不用讲解思路了

	public static void main(String[] args) {
        String str = "1_2_4_#_#_5_#_#_3_6_#_8_#_#_7_#_#";
        String[] arr = str.split("_");

        Queue<String> temp = new LinkedList<>();
        for (int i = 0; i < arr.length; i++) {
            temp.offer(arr[i]);
        }
        
        Tree head = startBuild3(temp);
        TestSearch(head);
    }

	static TreeHasParent startBuild3(Queue<String> queue) {
        String a = queue.poll(); 
        if (a.equals("#")) {
            return null;
        }
        TreeHasParent head = new TreeHasParent(Integer.valueOf(a));
        head.left = startBuild3(queue);
        head.right = startBuild3(queue);
        return head;
    }

总结

没有总结,上面的序列化非序列化都是建立在先序遍历的机制上面,如果您想要使用其他的遍历方法的话请参考上面的思维,可以自己再写出中序遍历,后序遍历的序列化和反序列化的代码!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

树的序列化与反序列化java - Kaiqisan 的相关文章

  • ajax多文件上传插件,jquery多文件上传插件

    jquery imageuploader js是一款jquery多文件上传插件 该jquery多文件上传插件主要用于上传图片 它允许你选择多个图片文件 也可以直接拖拽图片到指定区域 然后显示图片的预览图和信息 最后通过Ajax一次性上传选择
  • 面向对象这么久了,还没找到对象?

    写代码的小伙伴们真幸福啊 想要对象了 没问题 new一个就好了 但是 new太多对象 对象也会生气的哦 你瞧 她来了 从两段代码发现端倪 我们来计算一个矩形的面积 看看这两段代码有什么区别呢 第一段 const height 3 const

随机推荐

  • #R语言# 并行计算-foreach

    最近经常要用R跑程序 每次都要跑好久 不加并行 CPU利用率实在低 在此记录下相关的语句 先导入相关包 library foreach library doParallel library parallel no cores lt dete
  • “执行文化”向“创业文化”的转变( 15年6月)

    社长在15年度事业方针里面提到 事业计划不再是军令 考核事业部的是ROI 这句话 怎么理解 谁也没有解释过 所以我也不太理解具体的内容 但我们可以看看国内的家电企业 比如海尔在做什么 海尔提出过许多新的词汇 网格化 去中心化 海尔开放平台
  • feign-引入-服务之间的调用

    总结 1 导入openfeign的包 2 创建一个接口 FeignClient配置服务的名称 配置调用的服务的url 拷贝调用服务接口过来修改 3 启动类上打上 EnableFeignclients注解 是否配置包的原则 feign所在包和
  • 日历中的数字

    题目描述 ElemenT马上就要毕业了 他打开日历看了看时间 发现日历上的日期都是2017 04 04这样的格式的 月和日如果不足2位数 前面都会补充0 给定一个年份和月份 ElemenT把那个月的日期都按上述格式写到纸上 他现在想知道某种
  • 没有免费的午餐定理和丑小鸭定理

    没有免费的午餐定理 noerfelunhchtocerm 简称NFL 该定理由wolpert和Macerday提出 结论是由于对所有可能函数的相互补偿 最优化算法的性能是等价的 该定理暗指 没有其它任何算法能够比搜索空间的线性列举或者纯随机
  • libevent使用点滴(1)使用libevent调用evthread_use_pthreads的一个可能的内存泄露

    使用libevent时为了保证线程安全 提供了evthread use pthreads函数 他的内部是会分配内存的 但是没有对应的函数来反释放evthread use pthreads分配的内存 那么在如下的场景用evthread use
  • [007]爬虫系列

    一 备注 在阅读此文章前 请先阅读前两篇 007 爬虫系列 猿人学爬虫攻防大赛 第五题 js混淆 乱码增强 上 007 爬虫系列 猿人学爬虫攻防大赛 第五题 js混淆 乱码增强 中 本篇文章某个流程出了问题 即 直接贴代码 最后可能返回40
  • eclipse怎么查看开发包jar里源代码

    最近我打算学习一下谷歌的类库Guava 下载了Guava r09 jar包及其源码 为了可以方面的 看其源码 我将其源码导入 下面是导入的方法 我用的是eclipse 在Eclipse查看开发包jar源码的方法如下 1 选择项目 右键中单击
  • QDialog的相关API函数

    目录 常用的一些 API 函数 QDialog 的子类 QMessageBox QFileDialog QFont 字体类 QColorDialog QInputDialog QProgressDialog 总结 QDialog是Qt框架中
  • 单片机之瑞萨RL78 串口通信的例子

    瑞萨RL78 串口通信 瑞萨RL78 G1D单片机的串口收发数据的例子 在此示例中 我们将使用串口0 波特率为9600 include rl78g1d h define UART0 RECEIVE BUFFER U0RBR define U
  • SpringBoot集成Hasor-Dataway数据查询接口

    目录 一 前言 1 Hasor Core Core 容器框架 设计思想 特性 2 Hasor Web Web 框架 3 Hasor DB JDBC 框架 特性 4 Hasor DataQL DataQL 服务查询引擎 设计思想 特性 数据类
  • python中print的本质_python数据分析、挖掘常用工具,让你看到不一样的数据分析...

    Python语言 简要概括一下Python语言在数据分析 挖掘场景中常用特性 列表 可以被修改 元组 不可以被修改 字典 结构 集合 同数学概念上的集合 函数式编程 主要由lambda map reduce filter 构成 Python
  • 用两个stack实现queue

    stack和queue都是一种线性结构 要用stack实现queue的push和pop方法 我们首先需要了解下这两种结构的特点 stack 数据先进后出 queue 数据先进先出 我们记两个stack分别是head tail 我们的想法是这
  • Vue3中toRef函数与toRefs函数

    在Vue 3中 toRef和toRefs用于处理响应式数据 toRef函数接受一个响应式对象和一个键 返回一个只读的Ref对象 这意味着当原始数据发生变化时 toRef创建的Ref对象也会更新 toRefs函数接受一个响应式对象 react
  • C语言中如何不通过第三变量交换a、b两个变量值

    要求不能使用第三变量来达到交换两个变量值呢 方法一 include
  • vue拖拽组件(app移动端)

    vue拖拽组件
  • 09-多窗口切换-window_handles

    1 常用方法 使用背景 有些网站点击链接会新打开一个tab 如下图打开了两个浏览器窗口 元素定位正确 调试时一直报错 原因是未切换到对应的窗口句柄 切换到对应的窗口句柄才可以正常操作 current window handle 获得当前窗口
  • 联想拯救者Y7000P2023 Ubuntu20.04网卡驱动AX211安装

    sudo apt install flex bison git clone https github com intel backport iwlwifi git cd backport iwlwifi cd iwlwifi stack d
  • 2021年字节跳动74道高级程序员面试,附大厂真题面经

    安卓开发大军浩浩荡荡 经过近十年的发展 Android技术优化日异月新 如今Android 11 0 已经发布 Android系统性能也已经非常流畅 可以在体验上完全媲美iOS 但是 到了各大厂商手里 改源码 自定义系统 使得Android
  • 树的序列化与反序列化java - Kaiqisan

    大家好 都吃晚饭了吗 我是Kaiqisan 是一个已经走出社恐的一般生徒 为什么引入这个概念 在计算机中 如果我们如果想要可视化一棵树 那会是非常困难的工作 所以 我们就想到了一种最简单的方法来表示一棵树 而且只使用字符串 也可以区分每一颗