数据结构学习(C++)——平衡二叉树(AVL树)【2】

平衡化

    显然的,平衡化后的子树应该是平衡的,以此为原则,很容易得知在各种情况下应该怎么旋转。

private:

       void L_Balance(BTNode<T>* &p)

       {

              if (p->right->bf == 1) R_Rotate(p->right);

             L_Rotate(p); current = p;

       }

       void R_Balance(BTNode<T>* &p)

       {

              if (p->left->bf == -1) L_Rotate(p->left);

              R_Rotate(p); current = p;

       }

他们也是对称的。

修改平衡因子

    这是整个AVL树能运转的核心,现在的教科书,也正是因为没有真正弄明白如何修改平衡因子,才搞的switch…case满天飞。平衡因子的变化发生在旋转中——正因为这样,旋转才能有平衡化的作用——所以,应该把修改平衡因子的工作放在旋转操作中,而不是放在平衡化中。让我们来看看可能的旋转会带来的平衡因子变化的情况:

左旋(旋转后p暂时没有没有改变)

右旋(旋转后p暂时没有没有改变)

旋转前p

旋转前t

旋转后p

旋转后t

旋转前p

旋转前t

旋转后p

旋转后t

2

0

1

1

2

0

1

1

2

1

0

0

2

1

0

0

2

2

1

0

2

2

1

0

1

0

0

1

1

0

0

1

1

1

1

1

1

1

1

1

1

1

0

2

1

1

0

2

    旋转的最初发生是因为bf==2bf==2,对bf==1或者bf==-1的旋转是为了平衡化的需要——平衡化时的旋转ptbf不能异号。表面看起来这张表很凌乱,似乎没什么规律,其实不然。

    对于左旋——p的右子树从t变成了t的左子树,显然p的右子树高度至少减1tbf代表了原来的t左右子树的高度差,如果t->bf<0,则p的右子树的高度还要减少|t->bf|t的左子树在原来的左子树上面又多了一个p,显然左子树高度至少加1。在p的平衡因子修改完之后,如果p->bf>0那么t的左子树高度还要增加p->bf

    综合起来就是++(p->bf) -= t->bf < 0?t->bf:0; ++(t->bf) += p->bf > 0?p->bf:0;

    对于右旋同理。–(p->bf) -= t->bf > 0?t->bf:0; –(t->bf) += p->bf < 0?p->bf:0;

    可以看到这也是对称的。

完整的AVL树实现

#define c_p current->parent

#define c_root (c_p?((c_p->left == current)?c_p->left:c_p->right):root)

#include “BSTree.h”

 <?xml:namespace prefix = o ns = “urn:schemas-microsoft-com:office:office” />

template <class T>

class AVLTree : public BSTree<T>

{

public:

       bool insert(const T &data)

       {

              if (!BSTree<T>::insert(data)) return false; const T* p = &data;

              while (current)

              {

                     current->bf += (current->data > *p)?1:-1;

                     if (current->bf == -2) L_Balance(c_root);

                     else if (current->bf == 2) R_Balance(c_root);

                     if (!current->bf) break;

                     p = &(current->data); current = current->parent;

              }

              return true;

       }

       bool remove(const T &data)

       {

              if (!BSTree<T>::remove(data)) return false; const T* p = &r_r_data;

              while (current)

              {

                     current->bf -= (current->data > *p)?1:-1;

                     if (current->bf == -2) L_Balance(c_root);

                     else if (current->bf == 2) R_Balance(c_root);

                     if (current->bf) break;

                     p = &(current->data); current = current->parent;

              }

              return true;

       }

private:

       void L_Balance(BTNode<T>* &p)

       {

              if (p->right->bf == 1) R_Rotate(p->right);

             L_Rotate(p); current = p;

       }

       void R_Balance(BTNode<T>* &p)

       {

              if (p->left->bf == -1) L_Rotate(p->left);

              R_Rotate(p); current = p;

       }

       void L_Rotate(BTNode<T>* &p)

       {

              BTNode<T>* t = p->right;

              t->parent = p->parent; p->parent = t; p->right = t->left;

              if (t->left) t->left->parent = p; t->left = p;

              ++(p->bf) -= t->bf < 0?t->bf:0; ++(t->bf) += p->bf > 0?p->bf:0;

              p = t;

       }

       void R_Rotate(BTNode<T>* &p)

       {

              BTNode<T>* t = p->left;

              t->parent = p->parent; p->parent = t; p->left = t->right;

              if (t->right) t->right->parent = p; t->right = p;

              –(p->bf) -= t->bf > 0?t->bf:0; –(t->bf) += p->bf < 0?p->bf:0;

              p = t;

       }

};

总结与启示

AVL树是个平衡的二叉树,使用对称的旋转来维持平衡,这也注定了对于它的其他操作也应该是对称的。但由于它不是很完美,因此插入和删除对外表现不那么对称(插入时一次平衡化就能平衡,删除时最坏的情况能一直调整到树根O(logN)),但他们内在的本质应该是对称的,正如上面给出的——所有的操作都是对称的。

促使我仔细的研究插入和删除的对称性,是出于我认定AVL树操作是对称的这一信念。这反映了一个人的哲学修养,我不想在此多谈哲学对于一个人的重要性,只是为那些认为马哲、毛概毫无用处的人惋惜。

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