树(tree)型结构是一类重要的非线性结构。树型结构反映了数据元素之间的层次关系和分支关系,很是相似于天然界中的树。java
树是由一个集合以及在该集合上定义的一种关系构成的。集合中的元素称为树的结点,所定义的关系称为父子关系。父子关系在树的结点之间创建了一个层次结构。node
树是N(N>0)个结点的有限集合。算法
集合中存在惟一的一个结点,称为树根(root),该结点没有前趋结点,除根结点之外其他结点分为M(M≥0)个互不相交的集合,其中每个集合都是一颗树,并称其为根的子树(sub tree)。数组
以下图,为T={R,A,B,C,D,E,F}的树型表示法数据结构
采用子树概念递归定义数为:树是由根节点和若干棵子树构成的。post
树中结点之间所存在的父子关系能够用于描述树型结构的逻辑特征:编码
树型结构是非线性结构。.net
祖先与子孙的关系则是对父子关系的延伸,其定义了树中结点的纵向次序。3d
有序树的兄弟关系的延伸,定义了树中结点的横向次序。指针
ADT表示为:TREE=(D,R)
其中D是具备相同特性的数据元素的集合;R是元素集合D上的关系集合。若是D中只含有一个数据元素,则R为空集。
下面是数的抽象数据类型的定义:
ADT Tree{ 数据对象D:D是具备相同性质的数据元素的集合。 数据关系R:若D=Φ则R=Φ;若D≠Φ,则R={H},H是以下二元关系: ①在D中存在一个惟一的称为根的元素root,它在H下无前驱; ②除root之外,D中每一个结点在H下都有且仅有一个前驱。 基本操做: getSzie(); //返回树的结点数。 getRoot(); //返回根结点。 getParent(x); //返回结点x的父结点。 getFirstChild(x); //返回结点x的第一个儿子。 getNextSibiling(x); //返回结点x的下一个兄弟结点。 getHeight(x); //返回以x为根的树的高度。 insertChild(x,child); //将结点child为根的子树插入树中,做为x的子树 deleteChile(x,i); //删除x的第i棵子树。 proOrder(x); //先序遍历 postOrder(x); //后续遍历 levelOrder(x); //按层遍历 }ADT Tree
树的经常使用表示方式:树型表示法、文氏图表示法、凹入图表示法以及广义表表示法。 以下图:
每一个结点的度均不超过2的有序树,称为二叉树(binary tree)。
二叉树的递归定义以下:
二叉树或者是一棵空树,或者是一棵由一个根结点和两棵互不相交的分别称为根的左子树和右子树的子树所组成的非空树。
二叉树中每一个结点的孩子数只能是0、1或2个,而且每一个孩子都有左右之分。
位于左边的孩子称为左孩子,位于右边的孩子称为右孩子;以左孩子为根的子树称为左子树,以右孩子为根的子树称为右子树。
二叉树结构与通常树结构的区别:
所以二叉树并不是是树的特殊情形,他们是两种不一样的数据结构。
每层结点都达到最大数的二叉树称为满二叉树。
能够对满二叉树的结点进行编号,约定编号从根结点起,层间自上而下,层内自左而右,逐层由1到n进行标号。
若一棵二叉树最多只有最下面的两层结点的度数能够小于2,而且最下一层上的结点都集中在该层最左边的若干位置上,则此二叉树称为彻底二叉树。
二叉树第i(i≥1)层上的结点树最多为2^(i-1)。
高度为k的二叉树最多有2^k-1个结点。
对任何二叉树T,设n0、n一、n2分别表示度数为0、一、2的结点树,则n0=n2+1。
由性质3能够推导出:
当n2=0时,n0=1。即该二叉树有一个起始结点和一个终端结点,其余各结点有一个父亲和儿子,二叉树退化为单链表。
有n个结点的彻底二叉树的高度为⎣log n⎦。
满二叉树原理 非空满二叉树的叶节点数等于其分支结点数加1。
一棵非空二叉树空子树的数目等于其结点数目加1.
二叉树的存储结构:顺序存储结构和连接存储结构。
二叉树的顺序存储结构是把二叉树的全部结点按照必定的次序顺序存储到一组包含n个存储单元的额空间中。
在二叉树的顺序存储结构中只存储结点的值(数据域),不存储结点之间的逻辑关系,结点之间的逻辑关系由数组中下标的顺序来体现。
二叉树顺序存储的原则是:无论给定的二叉树是否是彻底二叉树,都看做彻底二叉树,即按照彻底二叉树的层次次序把各个结点依次存储数组中。
如图为二叉树的顺序存储结构:
在顺序存储结构中,由某个结点的存储单元地址能够推出其父亲、左儿子、右儿子及其兄弟的地址,假设给定结点的地址为I,则:
顺序存储结构对彻底二叉树而言,既简单又节省存储空间。
通常二叉树为了能用结点在数组中的相对位置表示结点之间的逻辑关系,也必须按彻底二叉树的形式来存储树中的结点,这必然形成存储空间的浪费。
通常二叉树使用顺序存储结构会形成大量存储空间的浪费,因此通常二叉树的存储结构更多的采用连接的方式。
二叉树的连接存储结构中每一个结点由数据域和指针域两部分组成。
二叉树每一个结点的指针域有两个,一个指向左儿子,一个指向右儿子。使用此结构的二叉树的连接存储结构称为二叉链表。
能够在上述结点的结构中增长一个指向结点的父结点的指针域,采用此结点结构获得的二叉树存储结构称为三叉链表。
树的遍历(traversal)是按照某种次序访问树中的全部结点,且每一个结点刚好访问一次。
二叉树的遍历是以递归的方式进行,依递归的调用顺序的不一样,可分为3种不一样的遍历方式:前序遍历方式、中序遍历方式、后序遍历方式。
前序遍历(Preorder Traversal)是先遍历根结点,再遍历左子树,最后才遍历右子树。
操做步骤以下:
中序遍历(Inorder Traversal)是先遍历左子树,再遍历根结点,最后才遍历右子树。
操做步骤以下:
后续遍历(Postorder Traversal)是先遍历左子树,再遍历右子树,最后才遍历根结点。
操做步骤以下:
层次遍历是指从二叉树的第一层开始,从上至下逐层遍历,在同一层中,则按从左到右的顺序对结点逐个访问。
操做步骤以下:
利用空指针域存放结点的前趋结点和后继几点的指针信息,这种附加的指针称为线索。
增长了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded Binary Tree)
为了区分一个结点的指针是指向去儿子仍是指向其前趋后继,为每一个结点增长了两个线索标志域ltag和rtag,这样线索链表的结点结构以下图:
标志域标志的是左右指针域的功能,以下:
每一个标志位只占1bit。
一棵二叉树以某种方式遍历并使其变成线索二叉树的过程称为二叉树的线索化。 以下图:三种遍历下的线索二叉树
为了讨论算法方便起见,一般在二叉树中增长一个与树中结点相同类型的头结点,令头结点的信息域为空,其lchild域指向二叉树的根结点,当二叉树为空时,lchild域值为空;其rchild域指向以某种方式遍历二叉树时最后访问的结点,当二叉树为空时,rchild域指向该结点自己,同时令原来指向二叉树根结点的头指针指向该头结点,以某种方式遍历二叉树时,第一个被访问结点的左指针域和最后一个被访问结点的右指针域的值若是是线索,也指向该头结点。
如图:
建立线索二叉树实质上就是遍历一棵二叉树,在遍历的过程当中,检查当前结点的指针域是否为空,若是为空改成指向前趋结点或者后继结点的线索。
在堆一个二叉树加线索时,必须申请一个头结点,创建头结点与二叉树的根结点的线索,对二叉树线索化后,还需创建最后一个结点与头结点之间的线索。
总结起来就两个步骤:
若是该结点的左标志位为1,那么其左指针域所指向的结点即是它的前趋结点。
若是该结点的左标志位为0,代表该结点有左儿子,根据中序遍历的定义,它的前趋结点是以该结点的左儿子为根的子树的最右结点。
若是该结点的右标志位为1,那么其右指针域所指向的结点就是它的后继结点。
若是该结点的右标志位为0,代表该结点有右儿子,根据中序遍历的定义,它的后继结点就是以该结点右儿子为根的子树的最左结点。
顺序遍历比对数据便可。
插入结点可分为两种状况考虑:
每种状况又分别分为两种:
将一颗树转换为二叉树的步骤以下:
转换过程以下图:
在转换事后的二叉树中,左分支上的各结点在原来树中是父子关系,右分支上的各结点在原来的树中是兄弟关系。
树转换为二叉树这一转换过程是可逆的,能够依据二叉树的根结点有无右儿子结点,将一颗二叉树还原为树。
步骤以下:
以下图即为还原过程:
森林是若干棵树的集合,森林亦可用二叉树表示。
转换步骤以下:
过程如图:
树的遍历分为两种方式:先根遍历和后根遍历。
先根遍历定义为:
后跟遍历的定义为:
根据树与二叉树的转换关系以及树与二叉树的遍历定义能够推出:
所以,树的遍历算法也可采用相应的二叉树的遍历算法实现。
森林的遍历有三种方式:前序遍历、中序遍历和后续遍历。
前序遍历的定义为: 若森林非空,则:
中序遍历的定义为: 若森林非空,则:
后续遍历的定义为: 若森林非空,则:
根据森林与二叉树的转换关系以及森林和二叉树的遍历定义能够推出:
以一组连续的存储单元来存放树中的结点,每一个结点有两个域:一个是数据域,用来存放结点的信息,另外一个是双亲域,用来存放双亲的位置。 结构如图所示:
将一个结点全部孩子连接成一个单链表形,而树中有若干个结点,则由若干个单链表,每一个单链表有一个表头结点,全部表头结点用一个数组来描述。 结构如图:
如图所示:
相似二叉链表,但第一根链指向第一个孩子,第二根链指向下一个兄弟。 结构如图:
哈夫曼(Huffman)树又称最优树,能够用来构造最优编码,用于信息传输、数据压缩等方面,是一类有着普遍应用的二叉树。
在数据通讯中,常常须要将传送的文字转换成由二进制字符0、1组成的二进制串,咱们称之为二进制编码。 在发送端,须要将电文中的字符转换成二进制的0、1序列,而在接受端则要将受到的0、1序列转换成对应的字符序列。
哈弗曼树可用于构造使电文的编码总长最短的编码方案:
规定哈弗曼树中的左分支表明0,右分支表明1,则从根结点到每一个叶节点所通过的路径分支组成的0或1序列便为该结点对应字符的编码,称为哈夫曼编码。
在哈夫曼编码树中,树的带权路径长度的含义是各个字符的码长与其出现次数的乘积和,也就是电文的代码总长。
由于哈夫曼算法构造的是带权路径长度最小的二叉树,因此采用哈夫曼树构造的编码是一种能使电文代码总长最短的不等长编码。
实现哈夫曼树的算法可分为两大部分,构造哈夫曼树和在哈夫曼树上求叶结点的编码。
在构造哈夫曼树时,能够设置一个结构数组huff_node保存哈夫曼树中各结点的信息,根据二叉树的性质可知,huff_node数组的大小可设置为2n-1。 数组元素的结构以下:
上一篇:数组和广义表
下一篇:图(graph)