AVL树规定其每种结点的左子树和右子树的高度最多差1,把必须重新平衡的节点称为å.对于二叉树

把必须另行平衡的节点称为å.对于二叉树,å的两棵子树的莫斯中国科学技术大学学最多相差2,那种不平衡或者有多样状态:

AVL(Adelson-Velskii and 奥迪(Audi)is)树是含有平衡条件(balance
condition)的二叉查找树。这一个平衡条件必须不难保证,而且必须有限支撑树的深度是O(logN)。AVL树规定其每一种结点的左子树和右子树的万丈最多差1。如下图,左侧的树是AVL树,左边的则不是。

  • 对å的左外孙子的左子树举行插入节点(左-左)
  • 对å的左外甥的右子树实行插入节点(左-右)
  • 对å的右外甥的左子树进行插入节点(右-左)
  • 对å的左外孙子的右子树进行插入节点(右-右)

图片 1

对此左-左和右-右须要单旋转(single
rotation)即可形成调整.对于左-右和右-左则供给双转悠(souble
rotation)即可落成调整.

AVL树的每一种结点(在其结点结构中)保留高度音讯。能够印证,一般景色下,一棵AVL树的中度最多为1.44log(N+2)
– 1.328,并且实际的惊人只比logN稍微多一些。

图片 2
图片 3
图片 4
图片 5

下图体现了一棵具有至少结点(143)高度为9的AVL树。那棵树的左子树是可观为7且结点数最少的AVL树,右子树是高度为8且结点数最少的AVL树。能够观察在中度为h的AVL树中,最少结点数S(h)=S(h-1)

最后show the code:

  • S(h-2) + 1。对于h=0,S(h) =
    1;h=1,S(h)=2。函数S(h)与斐波那契数密切相关,因而可以生产上面的有关AVL树的可观的界。
trait Tree{self=>
  import Tree.compare

  //AVL树 左左插入节点 左左单旋转
  def singleRotateWithLeft:Tree=self match {
    case Node(valueK2,leftK2,rightK2)=> leftK2 match {
      case Node(valueK1,leftK1,rightK1)  =>Node(valueK1,leftK1,Node(valueK2,rightK1,rightK2))
    }
  }

  def singleRotateWithRight:Tree=self match {
    case Node(valueK1,leftK1,rightK1) => rightK1 match {
      case Node(valueK2,leftK2,rightK2) => Node(valueK2,Node(valueK1,leftK1,leftK2),rightK2)
    }
  }

  //左右插入新节点
  def doubleRotateWithLeft:Tree=self match {
    case Node(valueK3,leftK3,rightK3) => Node(valueK3,leftK3.singleRotateWithRight,rightK3).singleRotateWithLeft
  }

  //右左插入数据节点
  def doubleRotateWithRight:Tree=self match {
    case Node(valueK1,leftK1,rightK1) =>Node(valueK1,leftK1,rightK1.singleRotateWithLeft).singleRotateWithRight
  }

}

图片 6

除了插入操作(即使删除是懈怠删除),全体的AVL树操作都得以以时间O(logN)执行。当举办插队操作时,须求创新通向根节点路径上的这一个结点的平衡新闻,因为插入操作恐怕会破坏AVL树的平衡特性。若是AVL树的平衡被毁掉,就要举行考订,这里称其为旋转(rotation)。

倘使结点a的左子树和右子树失去平衡(高度差2),则或者的景观有上面4种:

1.
对a的左外孙子的左子树进行二回插入。

2.
对a的左儿子的右子树举行三次插入。

3.
对a的右孙子的左子树进行贰次插入。

4.
对a的右孙子的右子树实行3次插入。

情景1和4有关结点a对称,情况2和3关于结点a对称,所以理论上唯有二种情景,可是从编制程序的角度来看要么4种情状。

首先种情景是插入爆发在“外边”的事态(即“左-左”或“右-右”),该境况通过对树的二回单旋转(single
rotation)完毕调整。第二种处境是插入发生在“内部”的气象(即“左-右”或“右-左”),本场所通过某个复杂些的双旋转(double
rotation)达成调整。

单旋转

下图体现了对于情况1什么样进展单旋转。左侧是旋转前,左侧是旋转后。在左边的树中,子树X中新插队了二个结点使得结点k2不再知足AVL平衡性质,因为它的左子树比右子树深2层(图中间的虚线代表树的各层)。

图片 7

上海体育场地中为了使树恢复平衡,把子树X上移了一层,把子树Z下移了一层。这其实早就超过定额达成了AVL性质的供给。抽象的抒写一下单旋转的长河:把树看成是软乎乎灵活的,抓住子结点k1,用力摇动它,在地心引力的功能下,k1就改为了新的根。X和Z还是分别是k1的左外孙子和k2的右儿子。子树Y包罗原树中介于k1和k2以内的那么些结点,今后把它坐落新树中k2的左孙子的地点上。这样,全部对一一的供给都得到满意,旋转后获取的新树是一棵AVL树。不仅如此,其实上海教室中通过单旋转所取得的新树的万丈,和插入新节点(导致原树不平衡)以前原树的莫斯中国科学技术大学学一致,所以通向根节点的门道上的那多少个结点不须求更进一步调整(它们照旧是平衡的)。下图是一遍单旋转的例子:

图片 8

下图演示应于“意况4”的单旋转创新:

图片 9

上面演示三个更长一些的事例,从开首的空AVL树起来插入③ 、2和1,然后依序插入4到7。在插入1时,AVL性质在根处被磨损。于是在根结点与其左孙子之间进行叁回单旋转以改进这些难点。

图片 10

图中以虚线连接的那多个结点是旋转的重点。接着插入4和5,在插入5时破坏了结点3处的AVL性质,通过单旋转将其勘误。在编制程序的时候必须牢记:结点2的右外孙子非得另行安装以链接到4而不是3,那一点很不难忘记,从而致使树被损坏。

图片 11

继之插入6,根节点处失去平衡,因为它的左子树中度为0而右子树中度为2。在根节点2和结点4里头进行3次单旋转。

图片 12

随后插入7,再度实施单旋转。

图片 13

 

双旋转

对于平衡二叉树失衡的情况贰 、3,使用单旋转是对事情没有什么益处的。

图片 14

那儿亟待动用双筋斗(四回旋转:“左-右”或“右-左”) 图片 15

 

上海体育场所中,树B或树C中有一棵比D深两层。为了重新平衡,选取k2作为新的根,k1作k2的左外孙子,k3作k2的右孙子。与单旋转一样,把树苏醒到插入新节点此前的档次。下图是“右-左”双筋斗。

图片 16

 

持续单旋转时的言传身教,以倒序插入10-16,接着插入⑧ 、9。插入16时并不破坏树的平衡。插入15时唤起结点7的不平衡,那属于景况3,必要做一遍“右-左”双旋转。

图片 17

进而插入14,同样也急需做一次“右-左”双转悠。结合地方的“右-左双筋斗查对情况3”示意图,子树A的根结点为5,子树B为空树,子树C的根结点为14,子树D的根结点为16。

图片 18

 插入13,在根结点处失衡。由于13不在4和7中间,所以是上边介绍的平衡情状4(对失衡结点4的右外甥的右子树进行2遍插入),那里供给做三遍单旋转(左旋)。

图片 19

 插入12也急需做一回单旋转(右旋)。

图片 20

 接着插入1① 、十 、8(未作旋转图示),获得下边一棵近乎完美的平衡树。

图片 21

 末了插入9,供给在八 、玖 、10五个结点之间做“左-右”双旋转。

图片 22

在编制程序时,必要总结子树的万丈差。有些程序员会在结点的贮存结构中,扩展3个BF成员(Balance
Factor:平衡因子)。那样能够幸免平衡因子的双重总计,进步程序的质量,可是却丧失了先后的某个简明性。

上边是一片段例程:

struct AvlNode
{
    Comparable element;
    AvlNode   *left;
    AvlNode   *right;
    int       height;

    AvlNode( const Comparable & theElement, AvlNode *lt,
                                            AvlNode *rt, int h = 0 )
      : element( theElement ), left( lt ), right( rt ), height( h ) { }
};

/**
 * Return the height of node t or -1 if NULL.
 */
int height( AvlNode *t ) const
{
    return t == NULL ? -1 : t->height;
}

/**
 * Internal method to insert into a subtree.
 * x is the item to insert.
 * t is the node that roots the subtree.
 * Set the new root of the subtree.
 */
void insert( const Comparable & x, AvlNode * & t )
{
    if( t == NULL )
        t = new AvlNode( x, NULL, NULL );
    else if( x < t->element )
    {
        insert( x, t->left );
        if( height( t->left ) - height( t->right ) == 2 )
            if( x < t->left->element )
                rotateWithLeftChild( t );
            else
                doubleWithLeftChild( t );
    }
    else if( t->element < x )
    {
        insert( x, t->right );
        if( height( t->right ) - height( t->left ) == 2 )
            if( t->right->element < x )
                rotateWithRightChild( t );
            else
                doubleWithRightChild( t );
    }
    else
        ;  // Duplicate; do nothing
    t->height = max( height( t->left ), height( t->right ) ) + 1;
}

下边是单旋转函数rotateWithLeftChild的示意图和源码。rotateWithRightChild是对称的,所以未贴出。

图片 23 

/**
 * Rotate binary tree node with left child.
 * For AVL trees, this is a single rotation for case 1.
 * Update heights, then set new root.
 */
void rotateWithLeftChild( AvlNode * & k2 )
{
    AvlNode *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;
    k2 = k1;
}

上面是“左-右”双旋转函数doubleWithLeftChild示意图和源代码,doubleWithRightChild是对称的。

图片 24

/**
 * Double rotate binary tree node: first left child
 * with its right child; then node k3 with new left child.
 * For AVL trees, this is a double rotation for case 2.
 * Update heights, then set new root.
 */
void doubleWithLeftChild( AvlNode * & k3 )
{
    rotateWithRightChild( k3->left );
    rotateWithLeftChild( k3 );
}

Reference:

[1] 《数据结构与算法分析c++描述》

相关文章