数据结构 ———— 树
数据结构 ———— 树
摘要:在数据结构中,树是一种非常重要的存在,树有着众多的变体,其中二叉树就是最为重要的一种变体,我们在学习初期,遇到的最多的树便是二叉树,在各种方面应用的最多的也是二叉树,因此我们今天着重分析二叉树。
1.树的基本构成
在数据结构中,树的构造和我们现实生活中的树非常相似,只不过外形更加抽象罢了,在数据结构中的树中,同样存在根以及枝叶和分叉,接下来,我们先通过图形来看一看树的基本构成,以此来方便二叉树的学习,因为作为树中的特殊品种,二叉树自然也具备树一切的基本构成。
1.1.父节点
在树结构中,存在着像链表中一样的指向,在一个节点中,内部存在一个指向其他节点的指针,在链表中,如果A节点指向B节点,那么A节点被称为B节点的前驱,而B节点称之为A节点的后继,在树中情况发生了变化,如果A节点指向B节点,那么A节点称之为B节点的父节点或者双亲节点,B节点称之为A节点的孩子节点,如图所示:
在图中,A节点指向了B和C节点,因此B和C节点是A节点的孩子节点,A是B、C节点的父节点。
1.2.孩子节点
同样如上图所示,B节点是被A指向的,因此B节点是A节点的孩子节点,而D节点被B指向,则D节点是B节点的孩子节点,孩子节点和父节点之间的关系是相互的,二者是一个对立关系。
1.3.兄弟节点
同样如上图所示,我们可以发现B节点和C节点的父节点是同一个,都是A节点,具备这种父节点是同一个节点关系的节点,相互之间互称兄弟节点,B和C之间就是兄弟节点,这一点非常类似人类之间的血统关系,一对父母所生的不同孩子,互称兄弟,而树中也沿用了这个定义。
1.4.堂兄弟节点
这个概念不是很重要,但是也是存在这个定义的,和人类之间的血统关系一样,如果两个男人互为兄弟,组成的不同家庭所生出的孩子之间互称堂兄弟姐妹,树中也沿用了这个定义,如图所示:
D和E,D和F之间,便是堂兄弟关系,但是E和F是亲兄弟,简而言之:两节点的父节点如果是亲兄弟,那么二者互为堂兄弟的关系。
1.5.根节点
具备了上面所讲的定义,那我们现在也可以讲解一下什么是根节点了,所谓根节点,就是一棵树中,位于最顶端的,是所有的节点之祖的节点,根节点没有双亲节点或者说是父节点,没有节点指向根节点,只有它指向了别人,如图所示:
红框中的节点,便是这棵树的根节点,该节点位于该树的最顶端,没有其他的节点指向它,它是这棵树的祖宗,如果没有它,那这棵树中的其他节点都无法被找到,根节点正好比每个家族中的祖宗,有了祖宗,才可能有现在的我们,而如果我们观察家谱的话也会发现,家谱实际上就是一个树状结构,而位于这棵树的上层的人,通常是我们的祖辈,而我们的家谱越往上,人数便会越少,如果有可能的话,最上方可能只有一对夫妇,而这一对夫妇正有可能是某一个家族的创始人。有理论提到,现在地球上的所有生物,都来源于一个细胞,该理论与根节点也有相似之处。总之,一棵树中位于最上方的,没有双亲节点的节点,我们称之为根节点。
1.6.叶子节点
和一棵真正的树一样,数据结构中的树也是有着叶子的,我们在生活中观察便可发现,一棵树有着很多的树干,树枝,这些树枝有着复杂的分叉,但是这些分叉都不约而同的在叶子部分结束掉了,也就是说叶子是一棵树中的最末端,在叶子之后,不会生出其他的分支了。这个概念被数据结构沿用,位于一棵树的最低端,没有孩子节点的节点,我们称之为叶子节点,如图所示:
在改图中,蓝色方框框起来的节点们就是叶子节点,这些节点位于树的最下一层,他们之后便没有其他节点了,这就是叶子节点,没有孩子节点的节点便是叶子节点。这好比我们的血统关系中,一个家族中的最小辈分,还没有孩子的人,就是这个家族中的叶子节点部分。需要注意的是位于最下层并不是叶子节点的必要条件,叶子节点不一定位于最下层,成为叶子节点的必要条件是没有孩子节点。
1.7.非叶子节点
与叶子节点相反的便是非叶子节点,如上图所示,蓝色方框以外的所有节点都是非叶子节点,根节点也是非叶子节点,只不过根节点是非叶子节点中比较特殊的一个罢了。
2.树节点的构成
了解了树的逻辑结构之后,我们来从物理存储的角度了解一下树中的每个节点到底是什么样子的,是什么样的数据构造使得数据结构中可以存在树这样的特殊结构呢?首先我们先来复习一下链表节点的构造:
在链表中,每个节点都是一个单元,拥有自己的地址。而每一个节点中,都有一个指针,名为next,这个指针是一个引用类型变量,它会指向另外一个单元的地址,这样就能够把数据串联起来了,因而我们仅需要一个头指针,就可以根据它逐步获取到所有单元的信息,上图中就是链表的内部物理存储结构示意图。而树呢?实际上,树结构是一种逻辑存储结构,其物理结构的实现存在两种——链式结构、顺序结构,这两种结构在之后我们都会说明,但是在顺序结构存储的树中的节点没有链式结构中的节点那么复杂,因此在这里我们仅介绍链式结构的树节点,链式结构的树节点和链表节点有着相似之处,如下图所示:
这就是树的节点示意图了,在树的非连续性存储或者说是链式存储中,其单个节点的结构就是这样的:每个节点中具备两个以上的指针指向其他节点。而我们经常研究的二叉树的节点,就是被严格限制的树节点,二叉树的节点被限制为每个节点中必须有两个指针字段,指向其他的节点。
3.二叉树
二叉树是一种非常重要的数据结构,在上文中也提到了二叉树的节点构成是被限制的,即必须有两个指针指向其他的节点,简而言之,就是在二叉树中,每个节点最多有两个分叉,如下图所示的就是一个典型的二叉树:
在这个树中,每个节点最多有两个孩子,在二叉树的真实实现中,即使一个节点没有孩子,其内部也是需要有指针字段的定义的,关于这点大部分人认为是理所当然的常识,实际上这是为了便于节点的统一管理,以及树的扩展。
4.重要的树结构
4.1.满二叉树
可以说是要求最为严格的树结构,首先满二叉树必须是二叉树,其次满二叉树要求在整棵树中:第1层进有根节点,之后第i层节点数必须为第(i-1)行的二倍,可以仅有一层,如果我们按照这个规则随便画一棵树的话,那么满二叉树通常是如下图所示的:
在满二叉树中,只有一个孩子的节点是不允许存在的,同时我们可以发现,在满二叉树中,其节点个数永远是奇数。同时我们可以感性的理解为,满二叉树画出来的形状是一个完整的等腰三角形。满二叉树因此具备一些数学上的特殊性质,这些特殊性质,和其中的被限制的分叉规则相关,如下图所示:
如果我们在一棵满二叉树中按照从上到下,从左到右的顺序依次为每个节点编号,就会发现一个规律,那就是父节点的二倍,就是左孩子节点的编号,而父节点的二倍加一,就是右孩子节点的编号,也就是说:如果我们为一棵满二叉树进行从上到下,从左到右的依次编号时,左孩子的编号 = 根节点编号 * 2;右孩子的编号 = 根节点编号 * 2 + 1。这本质上是一个数学规律,来源于二叉树的特殊分叉规则。
需要注意的是,以上的满二叉树是国内满二叉树的定义,国外的满二叉树定义并不是这样,国外的满二叉树定义为:二叉树中节点要么有两个孩子节点,要么没有孩子节点,关于这个定义的强度,实际上是弱于国内的二叉树定义强度的,如果按照国外的二叉树定义法则,下图中的树也是满二叉树:
但是这种树并不具备上面所提到的数学规律,因此在之后的学习中我们还是要以国内的满二叉树定义为准,国外的定义使用需要视具体情况而定。
4.2.完全二叉树
完全二叉树的定义为:将节点从上到下,从左到右依次平铺的树。在这个定义中,重点要求了从上到下从左到右依次铺满,而没有要求整棵树必须完全铺满,因此规则限定比完全二叉树弱一些,在完全二叉树中,允许最后一层的节点数不满足满二叉树法则,如果将上面的n-1层看做一颗子树,那么这棵子树必须是一个满二叉树,我们看图说话:
如图所示是一棵典型的完全二叉树,其节点满足所有节点从上到下,从左到右依次平铺,但是我们可以注意到其最后一层的最后一个元素缺失了,也就是三角形缺了一角,在该树中,其节点的排列规则是按照满二叉树的排列规则排列的,因此其仍然具备满二叉树的数学特质,这个数学特质在二叉树的顺序存储中经常被使用到。我们可以发现在完全二叉树中,仅允许有一个非叶子节点拥有一个孩子,其余非叶子节点必须拥有两个孩子或者没有孩子,实际上,在这一点上完全二叉树的定义就把上面的国外满二叉树否决了。在国内的定义中,满二叉树必定是完全二叉树,而完全二叉树是满二叉树从最右下角,依次摘取节点而产生的树,此规律对国外定义不生效。
4.3.二叉排序树
二叉排序树又称二叉搜索树,其外形并没有被严格定义,也就是说二叉排序树可以不是满二叉树或者完全二叉树,二叉排序树的定义主要偏重于节点中的权重,或者说是值,二叉排序树严格定义在整棵树的任意一棵子树中,其根节点的值必须大于其左子树中的所有值,小于其右子树中的所有值(有时也可以反过来定义,定义为小于左子树中的所有值,并大于右子树中的所有值,视情况而定)。如下图,是一棵典型的二叉排序树:
4.4.平衡二叉树
平衡二叉树是一种专注于子树深度定义的树,在平衡二叉树中,要求整棵树中没一颗子树的根节点的左子树和右子树的深度之差不超过1,如下图所示:
上面就是典型的非平衡二叉树和平衡二叉树,平衡二叉树通常是一种对二叉排序树的优化结构,由于深度差过大是会影响其搜索效率,因此提出了平衡化概念,并将这种深度差不大的树定义为平衡二叉树,在一棵二叉排序树最为平衡的时候,其作为搜索结构的效率最高。
4.5.堆
我们大家都知道堆排序是一种很重要的排序,而其中用到的堆结构,也是一种重要的树结构,需要注意的是堆结构并没有那么复杂,其结构定义也比较简单:堆通常是一个完全二叉树,并且在排序中我们经常使用顺序存储的方式存储堆。在堆中,要求每一棵子树的根节点都要大于或小于其左右子树中的任意节点,其中每一棵子树根节点都大于左右子树的节点的堆被称为大顶堆,反之称为小顶堆。堆结构在之前我已在堆排序中进行了详细的解释,同时目前我本人对于堆这个结构的高级运用并不了解,也没有深入研究过堆,在此不深入描述,在之后我会更新笔记,写一些对于堆结构更深入的研究。
4.6.B树与B+树
二者均是基于二叉排序树的索引树,通常用于存储数据,提升搜索效率之用,其结构并不是太难理解,我们可以理解为多叉排序树与多叉排序树plus。二者均是多叉的,其中B树的数据是存储在每个节点之中的,而B+树的数据仅存储在叶子节点中,这个概念在很多大学中老师并不会特别深入的教,因为其虽然结构容易理解,但是实现却十分复杂且自由,也就是说在实现时其每个节点很可能相互不同,这就使得其复杂性陡然上升,况且我学的早已还给老师了,因此在这里不详细解读,我找到了一篇比较简明移动的文章,先附在此处,供我也是供大家学习。
4.7.红黑树
可以说是最为难懂的树,但是其应用也非常广泛,在一些非关系型数据库的存储结构中,就经常使用到红黑树,基于红黑树的思想,可以让查询时间变得非常短,但是其思想也比较晦涩难懂,在此我也不深入描述,只是做一个标记,提醒我也是大家以后一定要深入学习红黑树这一重要的知识点。