数据结构-AVL树

AVL树

【概念】

1、AVL树性质:是带有平衡条件这个平衡条件必须容易保持,必须保证树的平均深度为O(logN))二叉查找树

         一颗AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树。(空树的高度定义为-1)

         插入一个节点可能会破坏AVL树的特性。如果发生这种情况,那么就要把性质恢复后才认为这一步插入完成。

2、旋转

和其他的树结构一样,AVL树也有插入、删除等操作,这些操作都有可能破坏AVL树的性质,对于经过操作后不满足AVL树性质的,我们通过旋转来恢复其性质。

经过旋转后满足AVL树性质:

  1. 对于树中的每个节点X,它的左子树中所有关键字值小于X的关键字值,而它的右子树中的所有关键字值大于X的关键字值。
  2. 每个节点的左子树和右子树的高度最多差1

我们通过两种旋转来恢复AVL树的性质。

单旋转

经过在X子树上的插入操作后,节点K2不满足AVL树的平衡特性,左子树比右子树深2层。为了恢复AVL树的平衡性质,我们需要将X上移一层,而旋转操作还将Z下移了一层(此时其实超出了AVL特性的要求)。

如图为左左单旋转(对称情形类似)。

                          《数据结构-AVL树》 

但是,有些情形通过单旋转并不能修复:

                        《数据结构-AVL树》

如果新插入的节点在子树Y上,就算经过了上图中的左左单旋转K1节点仍然不平衡。此时我们需要利用双旋转(本质是两次单旋转)将其修复。

双旋转

如图是左右双旋转(对称情形类似)先在K1处经过右右单旋转,再在K3处经过左左单旋转,最后恢复AVL树的平衡性质。

                          《数据结构-AVL树》


【程序】

 #include "avltree.h" #include <stdlib.h> #include "fatal.h" struct AvlNode { ElementType Element; AvlTree Left; AvlTree Right; int Height; }; AvlTree MakeEmpty( AvlTree T ) { if( T != NULL ) { MakeEmpty( T->Left ); MakeEmpty( T->Right ); free( T ); } return NULL; } Position Find( ElementType X, AvlTree T ) { if( T == NULL ) return NULL; if( X < T->Element ) return Find( X, T->Left ); else if( X > T->Element ) return Find( X, T->Right ); else return T; } Position FindMin( AvlTree T ) { if( T == NULL ) return NULL; else if( T->Left == NULL ) return T; else return FindMin( T->Left ); } Position FindMax( AvlTree T ) { if( T != NULL ) while( T->Right != NULL ) T = T->Right; return T; } //计算AVL节点的高度的函数 static int Height( Position P ) { if( P == NULL ) return -1; else return P->Height; } //返回俩数中较大的一个 static int Max( int Lhs, int Rhs ) { return Lhs > Rhs ? Lhs : Rhs; } //当插入发生在“外边”的情况,我们可以对树进行一次单旋转恢复其AVL性质 //单旋转分两种:左左单旋转,右右单旋转 //左左单旋转情形,当在K2节点处不满足AVL树性质。 static Position SingleRotateWithLeft( Position K2 ) { Position K1; K1 = K2->Left; K2->Left = K1->Right; K1->Right = K2; K2->Height = Max( Height( K2->Left ), Height( K2->Right ) ) + 1; K1->Height = Max( Height( K1->Left ), K2->Height ) + 1; return K1; /* New root */ } //右右单旋转情形:在K1处不满足AVL树的性质 static Position SingleRotateWithRight( Position K1 ) { Position K2; K2 = K1->Right; K1->Right = K2->Left; K2->Left = K1; K1->Height = Max( Height( K1->Left ), Height( K1->Right ) ) + 1; K2->Height = Max( Height( K2->Right ), K1->Height ) + 1; return K2; /* New root */ } //当插入发生在“内部”的情况,我们可以对树进行双旋转来恢复其AVL性质 //双旋转分两种:左右双旋转和右左双旋转 //左右双旋转情形: //先右右单旋转再左左单旋转。 static Position DoubleRotateWithLeft( Position K3 )//左右 { /* Rotate between K1 and K2 */ K3->Left = SingleRotateWithRight( K3->Left ); /* Rotate between K3 and K2 */ return SingleRotateWithLeft( K3 ); } //右左双旋转情形: //先左左单旋转再右右单旋转 static Position DoubleRotateWithRight( Position K1 )//右左 { /* Rotate between K3 and K2 */ K1->Right = SingleRotateWithLeft( K1->Right ); /* Rotate between K1 and K2 */ return SingleRotateWithRight( K1 ); } //插入操作(所有的插入都是在叶子节点上进行的) AvlTree Insert( ElementType X, AvlTree T ) { if( T == NULL )//如果树为空,则直接插入 { /* Create and return a one-node tree */ T = malloc( sizeof( struct AvlNode ) ); if( T == NULL ) FatalError( "Out of space!!!" ); else { T->Element = X; T->Height = 0; T->Left = T->Right = NULL; } } else if( X < T->Element ) { T->Left = Insert( X, T->Left ); if( Height( T->Left ) - Height( T->Right ) == 2 )//判断AVL条件是否满足 if( X < T->Left->Element )//X < T->Right->Element与上边的X < T->Element来共同确定是单旋转还是双旋转 T = SingleRotateWithLeft( T );//左左 else T = DoubleRotateWithLeft( T );//左右 } else if( X > T->Element )// X > T->Element { T->Right = Insert( X, T->Right ); if( Height( T->Right ) - Height( T->Left ) == 2 ) if( X > T->Right->Element )//X > T->Right->Element与上边的X > T->Element来共同确定是单旋转还是双旋转 T = SingleRotateWithRight( T );//右右 else T = DoubleRotateWithRight( T );//右左,T为失衡节点 } /* Else X is in the tree already; we'll do nothing */ T->Height = Max( Height( T->Left ), Height( T->Right ) ) + 1; return T; } ElementType Retrieve( Position P )//取位置P处的元素 { return P->Element; }

以上为部分关键代码,完整程序代码可以在https://github.com/mazilaile下载

【程序分析】

结合插入程序分析单双旋转例程:

单旋转程序分析:

                     《数据结构-AVL树》

向上图树中插入6,插入程序AvlTree Insert( ElementType X, AvlTree T )先判断树不为空。([1层]为递归调用的层次

[1层]再判断插入的元素大于元素T(即根处的元素5),递归调用Insert( XT->Right )

[2层]再次进行判断树不为空,插入的元素小于元素T(即元素8),递归调用Insert( XT->Left )

[3层]再次进行判断树不为空,插入的元素小于元素T(即元素7),递归调用Insert( XT->Left )

[3层]此时,树空,直接将6插入,更新高度为0,返回上一层(即元素7所在的一层)。

[2层]进行判断,并没有破坏AVL树性质,更新高度为1,返回上一层(即元素8所在的一层)。

[1层]进行判断,破坏了AVL树性质,右子树高度比左子树高度大二。所以要进行旋转恢复AVL树的性质。那么采用单旋转还是双旋转?之前判断过插入元素是小于该层元素8的,再将插入   元素与该层元素的左子树上的根元素比较ifX < T->Left->Element ),如果小于则属于左左情形,进行左左单旋转恢复AVL树的性质,如果大于属于左右双旋转情形,则先进行右右旋转再进行左左旋转进行恢复AVL树的性质。很明显,这里属于左左情形,我们进行单旋转将AVL树的性质进行恢复:

此时,程序中的T即为8(也即SingleRotateWithLeft( Position K2 )函数中的K2),我们先将K2(即8)的左儿子7保存在K1中,再将K2(即8)的左儿子赋值为K1的右儿子(即为空),最后将K1的右儿子赋值为K2

最后,我们再将节点高度进行更新,这时我们便恢复了AVL树的性质。

                                                         《数据结构-AVL树》

双旋转程序分析

再来看,插入以后需要进行双旋转恢复AVL树的平衡性质。

                《数据结构-AVL树》                         

向上图树中插入14,插入程序AvlTree Insert( ElementType X, AvlTree T )先判断树不为空。

[1层]再判断插入的元素大于元素T(即根处的元素4),递归调用Insert( XT->Right )

[2层]再次进行判断树不为空,插入的元素大于元素T(即元素6),递归调用Insert( XT->Right )

[3层]再次进行判断树不为空,插入的元素小于元素T(即元素15),递归调用Insert( XT->Left )

[4层]再次进行判断树不为空,插入的元素大于元素T(即元素7),递归调用Insert( XT->Right )

[4层]此时,树空,直接将14插入,更新高度为0,返回上一层(即元素7所在的一层)。

[3层]进行判断,并没有破坏AVL树性质,更新高度为1,返回上一层(即元素15所在的一层)。

[2层]进行判断,并没有破坏AVL树性质,更新高度为2,返回上一层(即元素6所在的一层)。

[1层]进行判断,破坏了AVL树性质,元素6的右子树高度比左子树高度大二。所以要进行旋转恢复AVL树的性质。那么采用单旋转还是双旋转?之前判断过插入元素是大于该层元素6的,再将插 入元素与该层元素的右子树上的根元素比较ifX < T->Right->Element ),如果大于则属于右右情形,进行右右单旋转恢复AVL树的性质,如果大于属于右左双旋转情形,则先进行左左单旋转再进行右右单旋转来恢复AVL树的性质。很明显,这里属于右左情形,我们进行双旋转将AVL树的性质进行恢复:

此时,程序中的T即为6(也即DoubleRotateWithRight( Position K1 )函数中的K1),我们先对K16)的右子树(15)进行左左单旋转:调用程序SingleRotateWithLeft( Position K2 )图中的15(标号K3)即为这里函数的形参K2,我们先将K2(即15)的左儿子7保存在K1中,再将K2的左儿子赋值为K17)的右儿子(即为14),最后将K17)的右儿子赋值为K2。即得到下图:

         

《数据结构-AVL树》                                           

再对K1进行右右单旋转:SingleRotateWithRight( Position K1 )6即为这里的K1,我们先将K1(即6)的右儿子7保存在K2中,再将K1(即为6)的右儿子(即为7)赋值为K2的左儿子(即为空),最后再将K2(即为7)的左儿子赋值为K1(即为6)。

最后,我们再将节点高度进行更新,这时我们便恢复了AVL树的性质。《数据结构-AVL树》

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