[Data Structure] 数据结构中各种树

数据结构中有很多树的结构,其中包括二叉树、二叉搜索树、2-3树、红黑树等等。本文中对数据结构中常见的几种树的概念和用途进行了汇总,不求严格精准,但求简单易懂。

1. 二叉树

二叉树是数据结构中一种重要的数据结构,也是树表家族最为基础的结构。

二叉树的定义: \textbf{二叉树的定义:} 二叉树的定义:
二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有 2 i − 1 2^{i-1} 2i1个结点;深度为k的二叉树至多有 2 k − 1 2^k-1 2k1个结点;对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。


二叉树的示例: \textbf{二叉树的示例:} 二叉树的示例:
在这里插入图片描述


满二叉树和完全二叉树: \textbf{满二叉树和完全二叉树:} 满二叉树和完全二叉树:
满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点。也可以这样理解,除叶子结点外的所有结点均有两个子结点。节点数达到最大值,所有叶子结点必须在同一层上

满二叉树的性质:

1) 一颗树深度为h,最大层数为k,深度与最大层数相同,k=h;

2) 叶子数为 2 h 2^h 2h;

3) 第k层的结点数是: 2 k − 1 2^{k-1} 2k1;

4) 总结点数是: 2 k − 1 2^k-1 2k1,且总节点数一定是奇数。

完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1~(h-1)层) 的结点数都达到最大个数,第h层所有的结点都连续集中在最左边,这就是完全二叉树。

注:完全二叉树是效率很高的数据结构,堆是一种完全二叉树或者近似完全二叉树,所以效率极高,像十分常用的排序算法、Dijkstra算法、Prim算法等都要用堆才能优化,二叉排序树的效率也要借助平衡性来提高,而平衡性基于完全二叉树。

在这里插入图片描述


二叉树的性质: \textbf{二叉树的性质:} 二叉树的性质:

1) 在非空二叉树中,第i层的结点总数不超过 2 i − 1 2^{i-1} 2i1, i>=1;

2) 深度为h的二叉树最多有 2 h − 1 2^h-1 2h1个结点(h>=1),最少有h个结点;

3) 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;   
4)具有n个结点的完全二叉树的深度为log2(n+1);   
5)有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:
    若I为结点编号则 如果I>1,则其父结点的编号为I/2;
    如果2I<=N,则其左儿子(即左子树的根结点)的编号为2I;若2I>N,则无左儿子;
    如果2I+1<=N,则其右儿子的结点编号为2I+1;若2I+1>N,则无右儿子。  
6)给定N个节点,能构成h(N)种不同的二叉树,其中h(N)为卡特兰数的第N项,h(n)=C(2*n, n)/(n+1)。
7)设有i个枝点,I为所有枝点的道路长度总和,J为叶的道路长度总和J=I+2i。


2. 二叉查找树

二叉查找树的定义: \textbf{二叉查找树的定义:} 二叉查找树的定义:

又称为是二叉排序树(Binary Sort Tree)或二叉搜索树。二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
  1) 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  2) 若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
  3) 左、右子树也分别为二叉排序树;
  4) 没有键值相等的节点。


二叉查找树的性质: \textbf{二叉查找树的性质:} 二叉查找树的性质:
对二叉查找树进行中序遍历,即可得到有序的数列。


二叉查找树的时间复杂度: \textbf{二叉查找树的时间复杂度:} 二叉查找树的时间复杂度:
它和二分查找一样,插入和查找的时间复杂度均为 O ( l o g n ) O(logn) O(logn),但是在最坏的情况下仍然会有 O ( n ) O(n) O(n)的时间复杂度。原因在于插入和删除元素的时候,树没有保持平衡。我们追求的是在最坏的情况下仍然有较好的时间复杂度,这就是平衡查找树设计的初衷。
  二叉查找树的高度决定了二叉查找树的查找效率。


二叉查找树的插入与删除: \textbf{二叉查找树的插入与删除:} 二叉查找树的插入与删除:
二叉查找树的插入过程如下

1) 若当前的二叉查找树为空,则插入的元素为根节点;

2) 若插入的元素值小于根节点值,则将元素插入到左子树中;

3) 若插入的元素值不小于根节点值,则将元素插入到右子树中。

二叉查找树的删除,分三种情况进行处理:

1) p为叶子节点,直接删除该节点,再修改其父节点的指针(注意分是根节点和不是根节点),如图a;

2) p为单支节点(即只有左子树或右子树)。让p的子树与p的父亲节点相连,删除p即可(注意分是根节点和不是根节点),如图b;

3) p的左子树和右子树均不空。找到p的后继y,因为y一定没有左子树,所以可以删除y,并让y的父亲节点成为y的右子树的父亲节点,并用y的值代替p的值;或者方法二是找到p的前驱x,x一定没有右子树,所以可以删除x,并让x的父亲节点成为y的左子树的父亲节点。如图c。
在这里插入图片描述
在这里插入图片描述

3. 平衡二叉树

我们知道,对于一般的二叉搜索树(Binary Search Tree),其期望高度(即为一棵平衡树时)为 l o g 2 n log_2n log2n,其各操作的时间复杂度 O ( l o g 2 n ) O(log_2n) O(log2n)同时也由此而决定。但是,在某些极端的情况下(如在插入的序列是有序的时),二叉搜索树将退化成近似链或链,此时,其操作的时间复杂度将退化成线性的,即O(n)。我们可以通过随机化建立二叉搜索树来尽量的避免这种情况,但是在进行了多次的操作之后,由于在删除时,我们总是选择将待删除节点的后继代替它本身,这样就会造成总是右边的节点数目减少,以至于树向左偏沉。这同时也会造成树的平衡性受到破坏,提高它的操作的时间复杂度。于是就有了我们下边介绍的平衡二叉树。
 平衡二叉树定义: \textbf{ 平衡二叉树定义:}  平衡二叉树定义:

平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用算法有红黑树、AVL树等。在平衡二叉搜索树中,我们可以看到,其高度一般都良好地维持在 O ( l o g 2 n ) O(log_2n) O(log2n),大大降低了操作的时间复杂度。

最小二叉平衡树的节点的公式如下:

F ( n ) = F ( n − 1 ) + F ( n − 2 ) + 1 F(n)=F(n-1)+F(n-2)+1 F(n)=F(n1)+F(n2)+1

这个类似于一个递归的数列,可以参考Fibonacci数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量。

3.1 平衡查找树之AVL树

 AVL树定义: \textbf{ AVL树定义:}  AVL树定义:

AVL树是最先发明的自平衡二叉查找树。AVL树得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis,他们在 1962 年的论文 “An algorithm for the organization of information” 中发表了它。在AVL中任何节点的两个儿子子树的高度最大差别为1,所以它也被称为高度平衡树,n个结点的AVL树最大深度约 1.44 l o g 2 n 1.44log_2n 1.44log2n。查找、插入和删除在平均和最坏情况下都是 O ( l o g 2 n ) O(log_2n) O(log2n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在 O ( l o g 2 N ) O(log_2N) O(log2N)。但是频繁旋转会使插入和删除牺牲掉 O ( l o g 2 N ) O(log_2N) O(log2N)左右的时间,不过相对二叉查找树来说,时间上稳定了很多。


   AVL树的自平衡操作——旋转: \textbf{   AVL树的自平衡操作——旋转:}    AVL树的自平衡操作——旋转:
AVL树最关键的也是最难的一步操作就是旋转。旋转主要是为了实现AVL树在实施了插入和删除操作以后,树重新回到平衡的方法。下面我们重点研究一下AVL树的旋转。

对于一个平衡的节点,由于任意节点最多有两个儿子,因此高度不平衡时,此节点的两颗子树的高度差2.容易看出,这种不平衡出现在下面四种情况:

在这里插入图片描述1) 6节点的左子树3节点高度比右子树7节点大2,左子树3节点的左子树1节点高度大于右子树4节点,这种情况成为左左。

2) 6节点的左子树2节点高度比右子树7节点大2,左子树2节点的左子树1节点高度小于右子树4节点,这种情况成为左右。

3) 2节点的左子树1节点高度比右子树5节点小2,右子树5节点的左子树3节点高度大于右子树6节点,这种情况成为右左。

4) 2节点的左子树1节点高度比右子树4节点小2,右子树4节点的左子树3节点高度小于右子树6节点,这种情况成为右右

从图2中可以可以看出,1和4两种情况是对称的,这两种情况的旋转算法是一致的,只需要经过一次旋转就可以达到目标,我们称之为单旋转。2和3两种情况也是对称的,这两种情况的旋转算法也是一致的,需要进行两次旋转,我们称之为双旋转。


单旋转 \textbf{单旋转} 单旋转
单旋转是针对于左左和右右这两种情况的解决方案,这两种情况是对称的,只要解决了左左这种情况,右右就很好办了。图3是左左情况的解决方案,节点k2不满足平衡特性,因为它的左子树k1比右子树Z深2层,而且k1子树中,更深的一层的是k1的左子树X子树,所以属于左左情况。

在这里插入图片描述

为使树恢复平衡,我们把k2变成这棵树的根节点,因为k2大于k1,把k2置于k1的右子树上,而原本在k1右子树的Y大于k1,小于k2,就把Y置于k2的左子树上,这样既满足了二叉查找树的性质,又满足了平衡二叉树的性质。

这样的操作只需要一部分指针改变,结果我们得到另外一颗二叉查找树,它是一棵AVL树,因为X向上一移动了一层,Y还停留在原来的层面上,Z向下移动了一层。整棵树的新高度和之前没有在左子树上插入的高度相同,插入操作使得X高度长高了。因此,由于这颗子树高度没有变化,所以通往根节点的路径就不需要继续旋转了。


双旋转 \textbf{双旋转} 双旋转
  对于左右和右左这两种情况,单旋转不能使它达到一个平衡状态,要经过两次旋转。双旋转是针对于这两种情况的解决方案,同样的,这样两种情况也是对称的,只要解决了左右这种情况,右左就很好办了。图4是左右情况的解决方案,节点k3不满足平衡特性,因为它的左子树k1比右子树Z深2层,而且k1子树中,更深的一层的是k1的右子树k2子树,所以属于左右情况。
在这里插入图片描述
  为使树恢复平衡,我们需要进行两步,第一步,把k1作为根,进行一次右右旋转,旋转之后就变成了左左情况,所以第二步再进行一次左左旋转,最后得到了一棵以k2为根的平衡二叉树。

3.2 平衡二叉树之红黑树

红黑树详细解析:https://www.jianshu.com/p/e136ec79235c
https://mp.weixin.qq.com/s?src=11&timestamp=1592808340&ver=2415&signature=B05etVYArMo6S6maEDI9MTi3LJvhxnyx7pn1X2zPw686OxnLwlGjpuk1gyJrO-D-GxDHgovAxUD70iMv7ayeuZ51QE5pduaSKNfzru8tQ7MZ9bop8BgGCTkwsMAdRK&new=1

当在10亿数据进行不到30次比较就能查找到目标时,不禁感叹编程之魅力!人类之伟大呀! —— 学红黑树有感。

红黑树的定义: \textbf{红黑树的定义:} 红黑树的定义:
  红黑树是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它是在1972年由鲁道夫·贝尔发明的,称之为"对称二叉B树",它现代的名字是在 Leo J. Guibas 和 Robert Sedgewick 于1978年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在 O ( l o g n ) O(logn) O(logn)时间内做查找,插入和删除,这里的n是树中元素的数目。

红黑树和AVL树一样都对插入时间、删除时间和查找时间提供了最好可能的最坏情况担保。这不只是使它们在时间敏感的应用如实时应用(real time application)中有价值,而且使它们有在提供最坏情况担保的其他数据结构中作为建造板块的价值;例如,在计算几何中使用的很多数据结构都可以基于红黑树。此外,红黑树还是2-3-4树的一种等同,它们的思想是一样的,只不过红黑树是2-3-4树用二叉树的形式表示的。


红黑树的性质: \textbf{红黑树的性质:} 红黑树的性质:

红黑树,Red-Black Tree 「RBT」是一个自平衡(不是绝对的平衡,红黑树的平衡叫做黑色完美平衡)的二叉查找树(BST),树上的每个节点都遵循下面的规则,:

  1. 每个节点都有红色或黑色
  2. 树的根始终是黑色的 (黑土地孕育黑树根,😄)
  3. 没有两个相邻的红色节点(红色节点不能有红色父节点或红色子节点,并没有说不能出现连续的黑色节点
  4. 从节点(包括根)到其任何后代NULL节点(叶子结点下方挂的两个空节点,并且认为他们是黑色的)的每条路径都具有相同数量的黑色节点

瞬间懵逼?了解一下印象就行,开始玩魔方都是要照着魔方公式一点点玩的,多玩几次就熟悉了。红黑树也一样,红黑树有两大操作:

  1. recolor (重新标记黑色或红色)
  2. rotation (旋转,这是树达到平衡的关键)

我们首先以二叉查找树的方法增加节点并标记它为红色。如果设为黑色,就会导致根到叶子的路径上有一条路上,多一个额外的黑节点,这个是很难调整的(违背性质5)。但是设为红色节点后,可能会导致出现两个连续红色节点的冲突,那么可以通过颜色调换(color flips)和树旋转来调整。所以我们会先尝试 recolor,如果 recolor 不能达到红黑树的 4 点要求,然后我们尝试 rotation,其实红黑树的关键玩法就是弄清楚 recolor 和 rotation 的规则,接下来看看详细的算法公式吧 千万别着急记忆公式,有图示会逐步说明,就像魔方一样,多玩几次就懂了:假设我们插入的新节点为 X

  1. 将新插入的节点标记为红色

  2. 如果 X 是根结点(root),则标记为黑色

  3. 如果 X 的 parent 不是黑色,同时 X 也不是 root:
    3.1 如果 X 的 uncle (叔叔) 是红色
    3.1.1 将 parent 和 uncle 标记为黑色
    3.1.2 将 grand parent (祖父) 标记为红色
    3.1.3 让 X 节点的颜色与 X 祖父的颜色相同,然后重复步骤 2、3

在这里插入图片描述
跟着上面的公式走:

  • 将新插入的 X 节点标记为红色

  • 发现 X 的 parent § 同样为红色,这违反了红黑树的第三条规则「不能有两个连续相邻的红色节点」

  • 发现 X 的 uncle (U) 同样为红色

  • 将 P 和 U 标记为黑色

  • 将 X 和 X 的 grand parent (G) 标记为相同的颜色,即红色,继续重复公式 2、3

  • 发现 G 是根结点,标记为黑色

结束

刚刚说了 X 的 uncle 是红色的情况,接下来要说是黑色的情况

  1. 如果 X 的 parent 不是黑色,同时 X 也不是 root:
    3.2 如果 X 的 uncle (叔叔) 是黑色,我们要分四种情况处理
    3.2.1 左左 (P 是 G 的左孩子,并且 X 是 P 的左孩子)
    3.2.2 左右 (P 是 G 的左孩子,并且 X 是 P 的右孩子)
    3.2.3 右右 (和 3.2.1 镜像过来,恰好相反)
    3.2.4 右左 (和 3.2.2 镜像过来,恰好相反)

当出现 uncle 是黑色的时候我们第一步要考虑的是 旋转 ,这里先请小伙伴不要关注红黑树的第 4 条规则,主要是为了演示如何旋转的,来一点点看,不要看图就慌,有解释的😜:
左 左 情 况 左左情况

这种情况很简单,想象这是一根绳子,手提起 P 节点,然后变色即可

在这里插入图片描述
左 右 左右
左旋: 使 X 的父节点 P 被 X 取代,同时父节点 P 成为 X 的左孩子,然后再应用 左左情况
在这里插入图片描述
右 右 右右
与左左情况一样,想象成一根绳子

在这里插入图片描述
右 左 右左
右旋: 使 X 的父节点 P 被 X 取代,同时父节点 P 成为 X 的右孩子,然后再应用 右右情况
在这里插入图片描述

4. B树

B树也是一种用于查找的平衡树,但是它不是二叉树。
B树的定义: \textbf{B树的定义:} B树的定义:
B树(B-tree)是一种树状数据结构,能够用来存储排序后的数据。这种数据结构能够让查找数据、循序存取、插入数据及删除的动作,都在对数时间内完成。B树,概括来说是一个一般化的二叉查找树,可以拥有多于2个子节点。与自平衡二叉查找树不同,B-树为系统最优化大块数据的读和写操作。B-tree算法减少定位记录时所经历的中间过程,从而加快存取速度。这种数据结构常被应用在数据库和文件系统的实作上。

在B树中查找给定关键字的方法是,首先把根结点取来,在根结点所包含的关键字K1,…,Kn查找给定的关键字(可用顺序查找或二分查找法),若找到等于给定值的关键字,则查找成功;否则,一定可以确定要查找的关键字在Ki与Ki+1之间,Pi为指向子树根节点的指针,此时取指针Pi所指的结点继续查找,直至找到,或指针Pi为空时查找失败

B树作为一种多路搜索树(并不是二叉的):

1) 定义任意非叶子结点最多只有M个儿子;且M>2;

2) 根结点的儿子数为[2, M];

3) 除根结点以外的非叶子结点的儿子数为[M/2, M];

4) 每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)

5) 非叶子结点的关键字个数=指向儿子的指针个数-1;

6) 非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];

7) 非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;

8) 所有叶子结点位于同一层;

如下图为一个M=3的B树示例:
在这里插入图片描述

5. B+树

B + 树 的 定 义 B+树的定义 B+
B+树是B树的变体,也是一种多路搜索树:

1) 其定义基本与B-树相同,除了:

2) 非叶子结点的子树指针与关键字个数相同;

3) 非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);

4) 为所有叶子结点增加一个链指针;

5) 所有关键字都在叶子结点出现;

B+树的搜索与B树也基本相同,区别是B+树只有达到叶子结点才命中(B树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;

B + 树 的 性 质 B+树的性质 B+

  1. 所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的

  2. 不可能在非叶子结点命中;

  3. 非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;

  4. 更适合文件索引系统。

在这里插入图片描述

6. B*树

B*树是B+树的变种,相对于B+树他们的不同之处如下:

(1)首先是关键字个数限制问题,B+树初始化的关键字初始化个数是cei(m/2),b*树的初始化个数为(cei(2/3*m))

(2)B+树节点满时就会分裂,而B*树节点满时会检查兄弟节点是否满(因为每个节点都有指向兄弟的指针),如果兄弟节点未满则向兄弟节点转移关键字,如果兄弟节点已满,则从当前节点和兄弟节点各拿出1/3的数据创建一个新的节点出来;

所以,B*树分配新结点的概率比B+树要低,空间使用率更高。

7. Trie树

Tire树称为字典树,又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高

Tire树的三个基本性质:
  1) 根节点不包含字符,除根节点外每一个节点都只包含一个字符;
  2) 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
  3) 每个节点的所有子节点包含的字符都不相同。

Tire树的应用:

1) 串的快速检索

给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。

在这道题中,我们可以用数组枚举,用哈希,用字典树,先把熟词建一棵树,然后读入文章进行比较,这种方法效率是比较高的。

2) “串”排序

给定N个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出。用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。

3) 最长公共前缀

对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为求公共祖先的问题。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值