大话数据结构——树

一、树的定义

树(Tree)是n(n>=0)个结点的有限集。 n=0又称为空树。在任意一课非空的树中:(1)有且仅有一个特定的称为跟(Root)的结点;(2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)。
树是一种一对多的数据结构。
需要注意的是:
(1)当n>0时根结点是惟一的,不可能存在多个根结点。
(2)m>0时,子树的个数没有限制,但它们一定是互不相交的。如果相交,就不符合树的定义。
结点分类
结点拥有的子树称为结点的度。度为0的结点称为叶结点或终端结点;度不为0的结点称为非终端结点或分支结点。除根结点外,分支结点也称为内部结点。树的度是树内各结点的度的最大值。如图所示,树的最大结点是D的度,为3,则树的度也为3.
结点分类
树中结点最大的层次称为树的深度或高度
如果将树中结点的各子树看成从左到右是有次序的,不能互换的,则称该树为有序树,否则称为无序树
森林是m(m>=0)棵互不相交的树的集合。

二、树的存储结构

简单的顺序存储结构和链式存储结构表示不了树一对多的关系。试想,数据元素挨个的存储,谁是谁的双亲,谁是誰的孩子呢?
树有自己的三种不同表示法:双亲表示法、孩子表示法、孩子兄弟表示法。
1. 双亲表示法
在每个结点中,附设一个指示器指示双亲结点在数组中的位置。结点结构表如下。

dataparent

其中,data是数据域,存储结点的数据信息。而parent是指针域,存储该结点的双亲在数组的下标。
标出双亲,左右结点则构成树。
双亲表示法
2. 孩子表示法
先介绍一种多种链表表示法:每个结点有多个指针域,其中每个指针指向一棵子树的根结点。
方案一:
指针域的个数就等于树的度。

datachild1child2child3……childd

其中data是数据域,child1到childd是指针域,用来指向该结点的孩子结点。对于图6-4-1的树来说,树的度是3,所以指针域的个数是3。如下图。
多重链表表示法
这种方法对于树中各结点的度相差很大是,显然是浪费空间的,因为有很多的结点,它的指针域都是空的。不过如果树的各结点度相差很小时,那就意味着开辟的空间被充分利用了,这时存储结构的缺点反而变成了优点。
为了按需分配空间,我们考虑方案二。
方案二:
每个结点指针域的个数等于该结点的度。

datadegreechild2child2……childd

其中data是数据域,degree为度域,也就是存储该结点的孩子结点的个数,child1到childd为指针域,指向该结点的各孩子结点。如下图。
多重链表表示法
这种方案也有缺点,就是会给结点的维护带来麻烦。所以,我们提出孩子表示法。
孩子表示法: 把每个结点的孩子结点排列起来,以单链表作存储结构,则n个结点有n个孩子链表,如果是叶子结点则次单链表为空。然后n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中。如下图。
孩子表示法
为此,设计两种结点结构,一个是孩子链表的孩子结点,如下图。

childnext

其中child是数据域,用来存储某个结点在表头数组的下标。next是指针域,用来存储指向某结点的下一个孩子结点的指针。
另一个是表头数组的表头结点,如下表。

datafirstchild

其中data是数据域,存储某结点的数据信息。firstchild是头指针域,存储该结点的孩子链表头指针。
但是,这种方法也找不到某个结点的双亲。于是有了双亲孩子表示法。如下图。
双亲孩子表示法
3. 孩子兄弟表示法
任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此,我们设置两个指针,分别指向该结点的第一个孩子和词结点的右兄弟。

datafirstchildrightsib

其中data是数据域,firstchild为指针域,存储该结点的第一个孩子结点的存储地址,rightsib是指针域,存储该结点的右兄弟结点的存储地址。如下图。
孩子兄弟表示法

三、二叉树的定义

二叉树(Binary Tree)是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。
二叉树的特点:

  • 每个结点最多有两棵子树,所以二叉树中不存在大于2的结点。注意不是只有两棵子树,而是最多有。没有子树或者有一棵子树都是可以的。
  • 左子树和右子树是有顺序的,次序不能颠倒。就像人的左右手。
  • 即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。

    二叉树有5种基本形态:

    1. 空二叉树。
    2. 只有一个根结点。
    3. 根结点只有左子树。
    4. 根结点只有右子树。
    5. 根结点既有左子树又有右子树。

    斜树:只有左子树或者只有右子树。是一种特殊的线性表。

    满二叉树:所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上。
    满二叉树的特点有:

    1. 叶子只能在最下一层。出现在其他层就不可能达到平衡。
    2. 非叶子结点的度一定是2。否则就是“缺胳膊少腿了”。
    3. 在同样深度的二叉树中,满二叉树的结点个数越多,叶子树越多。

    完全二叉树:

    1. 叶子结点只能在最下两层。
    2. 最下层的叶子一定集中在左部连续的位置。
    3. 倒数第二层,若有叶子结点,一定都在右部连续位置。
    4. 如果结点度为1,则该结点只有左孩子,即不存在右子树的情况。
    5. 同样结点数从二叉树,完全二叉树的深度最小。

    判断一棵树是否是完全二叉树,心中默默给每个结点按照满二叉树的结构逐层顺序编号,如果编号出现空档,就不是完全二叉树,否则就是。
    满二叉树是特殊的完全二叉树。
    完全二叉树必须先满足左后满足右,缺的元素只能是满二叉树最下一层的,高度差小于或等于1。

四、二叉树的存储结构

(一)顺序存储结构
一般只有完全二叉树才考虑顺序存储结构,因为完全二叉树的严格性,可以充分利用顺序存储空间。其他二叉树都会造成空间的浪费,特别是右斜树。
(二)二叉链表
二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域,称为二叉链表。

lchilddatarchild

其中data是数据域,lchild和rchild都是指针域,分别存放指向左孩子和右孩子的指针。
二叉链表

五、遍历二叉树

二叉树的遍历(traversing binary tree)是指从根结点出发,按照某种次序一次访问树中所有结点,使得每个结点被访问一次且仅被访问一次。

  1. 前序遍历
    根–左子树–右子树
    前

  2. 中序遍历
    左子树–根–右子树
    中

  3. 后序遍历
    左子树–右子树–根
    后

  4. 层序遍历
    根–第一层从左到右–第一层从左到右……
    层

已知前序遍历序列和中序遍历序列,可以唯一确定一棵二叉树。
已知后序遍历序列和中序遍历序列,可以唯一确定一棵二叉树。
已知前序遍历序列和后序遍历序列,不可以唯一确定一棵二叉树。

六、线索二叉树

为了充分利用二叉链表的空指针,把空指针指向前驱和后继,这种指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树。
对二叉树一某种次序比那里时期变为线索二叉树的过程称作是线索化。线索化的过程就是在遍历的过程中修改空指针的过程。
为了判别某一结点的lchild是指向左孩子还是前驱,rchild是指向右孩子还是后继,引入ltag和rtag两个标志域。

lchildltagdatartagrchild

其中,ltag为0时指向该结点的左孩子,为1时指向该结点的前驱;rtag为0时指向该结点的右孩子,为1时指向该结点的后继;因为对6-10-1的图的二叉链表图可以修改如下图。
线索二叉树
如果所用的二叉树需经常遍历或查找结点时需要某种遍历序列中的前驱和后继,那么采用线索二叉链表的存储结构就是不错的选择。

七、二叉树的性质

  1. 在一棵高度为k的二叉树中,结点总数为至多为2k(k次方)-1

八、树、森林与二叉树的转换

(一)树转化为二叉树
1. 加线。在所有兄弟结点之间加一条连线。
2. 去线。对树中每个结点,只保留它与第一个孩子结点的连线,删除它与其他孩子结点之间的连线。
3. 层次调整。以树的根结点为轴心,将整棵树瞬时间旋转一定的角度。使之结构层次分明。之一第一个孩子是二叉树结点的左孩子,兄弟转换过来的孩子是结点的右孩子。
树转换为二叉树
(二)森林转换为二叉树
森林是由若干棵树组成的,所以完全可以理解为,森林中的每一棵树都是兄弟,可以按照兄弟的处理办法来操作,如下:
1. 把每个树转换为二叉树;
2. 第一棵树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树的根结点的右孩子,用连线连接起来。当多有的二叉树连接起来后就得到森林转化的二叉树。
森林转换为二叉树
(三)二叉树转换为树
二叉树转换为树是树转换为二叉树的逆过程。
1. 加线。若某结点的左孩子结点存在,则将这个左孩子的右孩子结点、右孩子的右孩子结点、右孩子的右孩子的右孩子的结点……哈,反正就是左孩子的n个右孩子结点都作为此结点的孩子。将该结点与这些右孩子结点用线连接起来。
2. 去线。删除原二叉树中所有结点与其右孩子结点的连线。
3. 层次调整。使之结构层次分明。
二叉树转换为森林
(四)二叉树转换为森林
判断一棵树是否能转换为一棵树还是森林,就看它是否有右结点,若有,就可以转为森林,否则,就是一棵树。
1. 从根结点开始,若右孩子存在,则把与右孩子结点的连线删除,再查看分离后的二叉树,若右孩子存在,则连线删除……,直到所有右孩子连线都删除为止,得到分离的二叉树。
2. 再将每棵分离后的二叉树转换为树即可。
二叉树转换为树
(五)树与森林的遍历
树的遍历分为两种方式:
1. 一种是先根遍历树,即先访问树的根结点,然后依次先根遍历根的每棵子树。
2. 另一种是后根遍历,即先依次后根遍历每棵子树,然后再访问根结点。
森里的遍历方式也分为两种方式:
1. 前序遍历:先访问森林中第一棵树的根结点,然后再依次先跟遍历根的每棵子树,再依次用同样方式遍历除去每一棵树的剩余树构成的森林。
2. 后序遍历:先访问森林中第一棵树,后根遍历的方式遍历每棵子树,然后再访问根结点,再依次同样方式遍历除去第一棵树的剩余树构成的森林。
树和森林的遍历可参看(三)(四)图中的笔记。
神奇的是,森林的前序遍历和二叉树的前序遍历结果相同,森林的后序遍历和二叉树的中序遍历结果相同。这也就告诉我们,当以二叉链表作树的存储结构时,树的先根遍历和后根遍历完全可以借用二叉树的前序遍历和中序遍历的算法来实现。

九、赫夫曼树

赫夫曼树:带全路径长度WPL最小的二叉树。
先取最小权值的结点作为叶子结点,逐级递增就能构造出哈夫曼树。把左结点标为0,右结点标为1,就能构造出赫夫曼编码。

十、题目

  1. 某棵完全二叉树上有699个节点,则该二叉树的叶子节点数为(350)。
    解析:n0=n2+1;
    n=n0+n1+n2=n0+n1+n0-1=699
    由于完全二叉树中度为1的节点只有0个或1个两种情况,所以,将0或1带入上面公式,整理后得: n0=(n+1)/2或者n0=n/2; 看看n是否能被2整除,能则用n0=n/2。否则用n0=(n+1)/2 既叶子节点为n0=(n+1)/2=350。
  2. 一棵有124个叶节点的完全二叉树,最多有(248 )个节点。
    解析:n0 = n2 + 1,于是度为2的结点个数123个
    完全二叉树中度为1结点个数最多1个
    因此该完全二叉树中结点最多有123 + 1 + 124 = 248个

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/386037.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大话数据结构——图

图(Graph)是由定点的又穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。 一、各种图的定义 …

【工作感悟】达内java大数据课程

前言 其实前几篇文章已经写了好多有关于Spring源码的文章,事实上,很多同学虽然一直在跟着阅读、学习这些Spring的源码教程,但是一直都很迷茫,这些Spring的源码学习,似乎只是为了面试吹逼用,我大概问过一些…

大话数据结构——查找

查找(Searching)是根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录)。 一、顺序表查找 顺序查找又叫线性查找,是最基本的查找技术,它的查找过程是:从表中…

【工作经验分享】java图片转文字

前言 又到一年金九银十之际。 Java作为目前用户最多,使用范围最广的软件开发技术之一。 Java的技术体系主要由支撑Java程序运行的虚拟机,提供各开发领域接口支持的Java,Java编程语言及许多第三方Jvav框架构成。 其中,以Java的虚拟器为今天的着…

【干货】java课程实战培训

开头 消息队列 RocketMQ 是阿里巴巴集团基于高可用分布式集群技术,自主研发的云正式商用的专业消息中间件,既可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性,…

【干货】mysql建表语句注释

前言 难道程序员的职业生命线是青春饭?答案是的。 35岁考虑转行,然后35岁又成了一个新人,而外国可以做到60岁,啥也不说了,可能是觉得中年大叔油腻,不及小鲜肉便宜,唉,可叹市场更新…

【干货】mysql查询重复数据sql

前言 本系列的目的是明明白白、彻彻底底的搞定日期/时间处理的几乎所有case。上篇文章铺设所有涉及到的概念解释,例如GMT、UTC、夏令时、时间戳等等,若你还没看过,不仅强烈建议而是强制建议你前往用花5分钟看一下,因为日期时间处…

【微信小程序】java最简单观察者模式

开头 对于一个Java程序员而言,能否熟练掌握并发编程是判断他优秀与否的重要标准之一。因为并发编程是Java语言中最为晦涩的知识点,它涉及操作系统、内存、CPU、编程语言等多方面的基础能力,更为考验一个程序员的内功。 那到底应该怎么学习并…

【性能优化实战】java验证码识别训练

前言 今天刚好有空,跟大家聊聊如何学好算法进大厂。 前两天一个读者和我说,他坚持刷算法题2个月,薪资翻番去了他梦寐以求的大厂,期间面字节跳动还遇到了原题…其实据我所知目前国内的大厂和一些独角兽,已经越来越效仿…

吸水间最低动水位标高_体验长安逸动EV460:再也不用为电动车续驶里程焦虑了...

文| 车突突车图腾出品,未经许可,谢绝转载● ● ●人们都在期待碧水蓝天,而且越来越多的消费者也开始践行环保理念,在买车时关注起了纯电动汽车。不过遗憾的是,纯电动汽车目前还没能成为主流。一方面,是因为…

java开发工具包jdk包括哪些

害怕干不过SpringBoot?莫慌,我送你套神级pdf文档 随着 Spring Boot 使用越来越广泛,Spring Boot 已经成为 Java 程序员面试的知识点,很多同学对 Spring Boot 理解不是那么深刻,经常就会被几个连环追问就给干趴下了&am…

微信计步器怎么不计步_难以关闭的微信朋友圈广告

太难关掉了。”试图关闭朋友圈广告的小曾,在对照着腾讯视频上的一个长达6分钟的视频演示之后,通过14次操作才得以关闭。这14步操作具体如下:点击“我”—点击“设置”—点击“关于微信”—点击“微信隐私保护指引”—下拉两个屏幕的面积—点击…

java开发工具有哪些

前言 Netty 是一款基于 Java 的网络编程框架,能为应用程序管理复杂的网络编程、多线程处理以及并发。Netty 隐藏了样板和底层代码,让业务逻辑保持分离,更加易于复用。使用 Netty 可以得到一个易于使用的 API,让开发人员可以专注自…

expdp导出 schema_记录一则expdp任务异常处理案例

在XTTS迁移测试阶段,遇到执行几个expdp的导出任务,迟迟没有返回任何信息,对应日志无任何输出。环境:AIX 6.1 Oracle 10.2.0.4现象:在XTTS迁移测试阶段,遇到执行几个expdp的导出任务,迟迟没有返…

java开发工具软件排行榜

前言: 都说学历是敲门砖,是一点都没错,即使是在重技术轻学历的互联网企业,面试官对于学历越高的程序员初印象会更好,面试也会更顺利,而大部分专科学历的程序员,除非有过硬的技术,否…

java开发工程师工作内容怎么写

什么是分布式锁?在回答这个问题之前,我们先回答一下什么是锁。 普通的锁,即在单机多线程环境下,当多个线程需要访问同一个变量或代码片段时,被访问的变量或代码片段叫做临界区域,我们需要控制线程一个一个…

community 计算模块度_光模块深度:国内光模块企业快速崛起

一、核心观点二、发展追溯:技术是底蕴、创新是动力1 光通信发展:技术迭代加快,国产替代是前进的方向依据摩尔定律,光模块的小型化、低成本以及高速率是产品迭代的主要方向。2 竞争格局:市场集中度高,巨头地位稳固,国内厂商稳步崛起…

java开发工程师的自我评价

前言 京东到家订单中心系统业务中,无论是外部商家的订单生产,或是内部上下游系统的依赖,订单查询的调用量都非常大,造成了订单数据读多写少的情况。 我们把订单数据存储在MySQL中,但显然只通过DB来支撑大量的查询是不…

华为魔术手机拆机图解_华为P9进水不显示维修案例

看点:iPhone X原装屏与国产屏有哪些区别?看点:换7P、8P屏幕:C11和DTP和DKH的区别狮淘:华人手机维修师专属工具集合店,不锈钢拆机片5个只需9.9元!包邮山猫潮品:手机渠道直供&#xff…

java开发工程师自我介绍文本

前言 每年金三银四,金九银十之际,想进阶梦想挑战大厂的朋友层出不穷。 梦想是要有的,万一就实现了呢?且撇开大牛们不说,每年面试之时问题也层出不穷,不得不说,每年被算法绝杀的朋友也是不在少数…