数据结构——伸展树与B树

  除了二叉查找树之外,还有许多其他类型的树,这些树因为其独特的性质,在很多特定的领域获得了广泛的应用。这里简单介绍两种特别的树,伸展树和B树。

伸展树

  伸展树是二叉查找树的扩展,是一种特殊的二叉查找树。但不同与普通的二叉平衡查找树如AVL树,伸展树没有严格的平衡条件,但是伸展树仍然可以保证其基本操作的摊还时间复杂度为O(logN)。要理解所谓的伸展树的摊还时间复杂度,需要从二叉查找树开始分析。对于没有平衡条件的二叉查找树而言,其基本操作(如查找操作)的平均时间复杂度为O(logN),但在最坏情况下(树为N个节点的线性链)时间复杂度为O(N)。为了降低最坏情况下的时间复杂度,提出了平衡树的概念可以将最坏情况下的时间复杂度降为O(logN)。但是这种平衡条件使树更为复杂,编程复杂度增加。为此,又提出了伸展树概念,伸展树没有类似于AVL树那样的平衡条件,但是仍然可以保证其基本操作的摊还时间复杂度为O(logN)。所谓摊还时间复杂度为O(logN)是指伸展树不保证其每次操作的时间复杂度都为O(logN),但是保证其M次操作的总共时间复杂度为O(MlogN)。这样就称为摊还时间复杂度为O(logN)。

  为了实现伸展树,基本的想法是当一个节点被访问后,它应该经过一系列的AVL树的旋转操作旋转到根上。(被访问过的节点下次继续被访问的可能性很大。)但是这样简单的旋转会造成其他在访问路径上的节点被推到一个较深的深度,使得下次访问此路径上的节点时花费更多时间,导致无法达到O(logN)的摊还时间复杂度。为了解决这个问题,将被访问节点推到根上时使用展开(也是一种旋转)的方式。此时分为三种情况,下面分别描述这三中情况及其旋转方法:

(1)            被访问节点是根节点的子节点:此时使用最简单的单旋转将其旋转到根节点即可。

(2)            被访问的节点有父节点和祖父节点,并且这三个节点呈之字形(左节点的右节点或者右节点的左节点):此时使用双旋转即可。

(3)            被访问的节点有父节点和祖父节点,并且这三个节点呈一字型(左节点的左节点或者右节点的右节点):此时使用一字型旋转。所谓的一字型旋转可以描述如下:G,P,X分别是三个祖父,父亲,被访问节点,一字型旋转后这三个节点仍然成一字型,只是X,P,G分别为祖父,父亲,儿子节点。

当一个节点被访问后,基于这三种情况选择处理方案,直到被访问节点被旋转到根处为止。基于上面描述的算法,可以证明这样的伸展树满足摊还时间复杂度为0(logN)的情况。

  另外描述一下伸展树的其他操作,伸展树的插入操作与普通二叉树的插入操作相同,不需要特殊的平衡条件限制。只是当执行了一次访问操作后需要展开(旋转)树。删除操作通过将要删除的节点提到根处,然后树分为TL与TR,删除跟并将TL中最大的节点提到根处(通过旋转),此时根处没有右子树,将TR作为新根的右子树即可。

B树

  B是也是一种查找树,即B树中存储的数据也是排序的,但是B树不是二叉树。B树定义如下:

  阶为M的B-树是一棵具有下列结构特征的树:

(1)   树的根或者是一片树叶,或者其儿子树在2和M之间。

(2)   除根外,所有非树叶节点的儿子树在M/2取上限和M 之间。

(3)   所有的树叶都在相同的深度上。

  B树中只有树叶节点中保存数据(有些类型的B树非叶节点中也保存数据,此处不考虑),非叶节点中保存的是子树的指针,以及一些表示顺序的关键字。B树在查找元素时通过与非叶节点中的关键字进行比较来确定要查找的数据所在的子树的方向,从而沿着树结构一直找到存储数据的叶节点。B树的非叶节点中保存其所有子树的指针,所以其子树数目是有限制的,同时树叶节点中关键字的数目也是有限制的。这样的性质导致在B树中插入关键字时需要考虑树叶节点可以保存的最大关键字数目。当树叶节点中关键字数目超过了限制,则树叶节点需需要分裂成两个节点,这样有需要考虑这些节点的父节点的子树数目限制,一旦超过了父节点可以拥有的子树的数目,那么父节点也需要分裂。有一种情况是节点的分裂操作一直向上传递到根节点处,并且根节点也需要分裂。此时产生一个新的节点作为根节点。这种情况也是导致B树增长的唯一情况。

  B树主要应用到数据库领域,作为索引的实现。此时,树叶节点中保存的数据应该是某些数据在磁盘中所处的位置。

你可能感兴趣的:(数据结构——伸展树与B树)