AVL树,伸展树,B-树,B+树,B*树

1,AVL树

AVL树是带有平衡条件的二叉查找树,我们知道二叉查找树的定义是对于树中任意的节点X,它的左子树的所有项的值都小于X的值,它的右子树的所有项的值都大于X的值。


带有平衡条件的二叉查找树,是为了解决一个什么问题呢?
  问题是:当向一棵树输入预先排序好的数据时,二叉查找树所有节点都没有左儿子,这时对这棵树的操作的代价就会很大。
  AVL树的平衡条件是:每个节点的左子树和右子树的高度最多差1。(空树的高度定义为-1)
  在建造AVL树的时候,每插入一个节点判断树是否平衡,当树不满足平衡条件时,进行简单的修正。
  两种修正方法:单旋转,双旋转。

  1,单旋转:
    对a的左儿子的左子树进行一次插入;对a的右儿子的右子树进行一次插入;使a的两颗子树的高度差为2;此时使用单旋转

  2,双旋转:
    对a的左儿子的右子树进行一次插入;对a的右儿子的左子树进行一次插入;使a的两颗子树的高度差为2;此时使用双旋转

  AVL树删除节点:

   a.当被删除节点n是叶子节点,直接删除。
   b.当被删除节点n只有一个孩子,删除n,用孩子替代该节点的位置。
   c.当被删除结点n存在左右孩子时,真正的删除点应该是n的中序遍历的前驱,或者说是左子树最大的节点,之后n的值替换为真正删除点的值。这就把c归结为a,b的问题。
  删除之后还要考虑是否平衡,进行相应的修正。

2,伸展树

伸展树保证从空树开始连续M次对树的操作最多话花费O(MlogN)时间,


解决的问题:因为要求O(logN)的摊还时间界,只要一个节点被访问,它就必须被移动,否则,一旦发现一个深层的节点,我们就有可能不断对它进行访问,如果这个节点不移动,而每次又花费O(N),那么M次访问将花费O(M*N)的时间。

伸展树的基本思想:当一个节点被访问后,它就要经过一系列AVL树的旋转被推到根上。

方法1:执行单旋转

    事实证明,对一个节点的访问会将另一个节点向深处推进,并没有改善访问路径上其他节点的状况。使用这个策略将会存在一系列M个操作共需要O(M*N)的时间,因此方法不够好。

方法2:展开


    思路与上面的旋转相似,但在旋转如何实施上我们稍微有些选择的余地。我们仍然从底部向上沿着访问路径旋转。

情形:
   1,之字形  使用AVL双旋转
   2,一字形  使用一字形旋转

对一个节点访问之后,它总是处于其中一种情形,使用对应的方法展开,直到将节点推到根上。

3,B-树(不读B减树,就叫B树,Banlanced-tree)

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.所有叶子结点位于同一层;

这些概念挺枯燥的。

4,B+树

B+树也是一种多路搜索树:
       1.其定义基本与B-树同,除了:
       2.非叶子结点的子树指针与关键字个数相同;
       3.非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);
       4.为所有叶子结点增加一个链指针;
       5.所有关键字都在叶子结点出现;

B+树是由B树和索引顺序访问方法演化而来,但是在现实使用过程中已经没有使用B树的情况了。B和B+树的区别在于,B+树的非叶子结点只包含导航信息,不包含实际的值,所有的叶子结点和相连的节点使用链表相连,便于区间查找和遍历。

5,B*树

B*树是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针。

  

    原文作者:AVL树
    原文地址: https://blog.csdn.net/u013014724/article/details/53470168
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞