8. AVL树
AVL树得名于其发明者G.M.Adelson-Velsky和E.M.Landis。AVL树是一个各结点具有平衡高度的扩展的二叉搜索树。在AVL树中,任一结点的两个子树的高度差最多为1,AVL树的高度不会超过
【结论】AVL树既有二叉搜索树的搜索效率又可以避免二叉搜索树的最坏情况(退化树)出现。
AVL树的表示与二叉搜索树类似,其操作基本相同,但插入和删除方法除外,因为它们必须不断监控结点的左右子树的相对高度,这也正是AVL树的优势所在。
下面是AVL树的结点的一种,其中,balanceFcator(平衡因子)的值必须是-1、0或1三者之一。
left |
data |
balanceFactor |
right |
struct AVLTreeNode
{
int data;
AVLTreeNode *left, *right;
int balanceFactor;
};
当然,也可以通过对TreeNode的继承来实现。
另一种AVL树的结点为:
left |
data |
height |
right |
其实现方法如下:
#include <stdafx.h>
#include <iostream>
using namespace std;
//AVL树可以从BST树继承,但是为了方便于应试,这里单独进行实现,以增加独立性。
struct AVLTreeNode
{
int data;
AVLTreeNode *left;
AVLTreeNode *right;
int height; //height是结点的高度,叶子结点的高度为0,其父结点的高度为1,以此类推……
//【注】不要将这里的高度(height)与深度的概念相混淆。
const int getHeight()const;
AVLTreeNode(const int _data=0, AVLTreeNode *_left=NULL, AVLTreeNode *_right=NULL, int _height=0)
:data(_data), left(_left), right(_right), height(_height){}
};
//定义该成员函数的目的是为了防止调用NULL的height成员时出现的未定义错误而导致程序崩溃
const int AVLTreeNode::getHeight()const
{
if(NULL == this)
return -1;//空结点的高度为-1
return this->height;
}
class AVLTree
{
public:
AVLTreeNode *root;
private:
//_clearAVLTree、_printAVLTree、_findAVLNode、copyAVLTree的实现与BST树的几乎完全相同
void _clearAVLTree(AVLTreeNode *node);//在clearTree和析构函数中会被用到
void _printAVLTree(AVLTreeNode *node, int level);//只能用在printTree函数中
void _insertAVLNode(AVLTreeNode *&node, AVLTreeNode *newNode);
//void _deleteAVLNode(AVLTreeNode *&node, int item);//AVL树的删除结点函数太难,从略。
AVLTreeNode *copyAVLTree(AVLTreeNode *node);
AVLTreeNode *_findAVLNode(AVLTreeNode *node, const int item)const;//只能用在findNode中
void singleRotateLeft(AVLTreeNode *&p);
void singleRotateRight(AVLTreeNode *&p);
void doubleRotateLeft(AVLTreeNode *&p);
void doubleRotateRight(AVLTreeNode *&p);
public:
AVLTree():root(NULL){};
//复制构造函数、赋值操作符、析构函数、复制树、打印树、清除树、查找结点的实现与BST树的相应实现几乎完全相同,
//仅仅由于AVL树的结点的数据比BST树多了个height成员变量才导致有所区别。
AVLTree(const AVLTree *rhs);
AVLTree & operator=(const AVLTree *rhs);
~AVLTree(){clearAVLTree();}
void printAVLTree();
void clearAVLTree();
AVLTreeNode *findAVLNode(const int item)const;
void insertAVLNode(const int item);
//void deleteAVLNode(const int item);//AVL树的删除结点函数太难,从略。
};
void AVLTree::_clearAVLTree(AVLTreeNode *node)
{
if(NULL == node)
return;
_clearAVLTree(node->left);
_clearAVLTree(node->right);
delete node;
}
void AVLTree::clearAVLTree()
{
_clearAVLTree(root);
root = NULL;
}
void AVLTree::_printAVLTree(AVLTreeNode *node, int level)
{
if(NULL == node)
return;
_printAVLTree(node->right, level+1);
for(int i=0; i<level; ++i)
cout << " ";
cout << node->data << endl;
_printAVLTree(node->left, level+1);
}
void AVLTree::printAVLTree()
{
_printAVLTree(root, 0);
}
AVLTreeNode *AVLTree::copyAVLTree(AVLTreeNode *node)
{
if(NULL == node)
return NULL;
AVLTreeNode *newNode = new AVLTreeNode(node->data, copyAVLTree(node->left), copyAVLTree(node->right), node->height);
return newNode;
}
AVLTree::AVLTree(const AVLTree *rhs)
{
root = copyAVLTree(rhs->root);
}
AVLTree & AVLTree::operator=(const AVLTree *rhs)
{
if(this == rhs)
return *this;
clearAVLTree();
copyAVLTree(root);
return *this;
}
AVLTreeNode *AVLTree::_findAVLNode(AVLTreeNode *node, const int item)const
{
if(NULL == node)
{
cerr << "Can't find it\n";
exit(1);
}
if(item == node->data)
return node;
else if(item < node->data)
return _findAVLNode(node->left, item);
else
return _findAVLNode(node->right, item);
}
AVLTreeNode *AVLTree::findAVLNode(const int item)const
{
return _findAVLNode(root, item);
}
/********************************************************************
singleRotateRight的图示:
node lcNode
/ / \
lcNode ==> newAVLNode node
/ \ /
newAVLNode lcNode->right lcNode->right
【思想】newAVLNode与lcNode、node在一条线上,并在左子树,故采用单右旋以调整高度使之达到平衡。实际中,如果AVL树在node点不平衡的话,lcNode->right肯定为NULL。
*********************************************************************/
void AVLTree::singleRotateRight(AVLTreeNode *&node)
{
//N:node, LC(left child):lcNode
AVLTreeNode *lcNode = node->left;
node->left = lcNode->right;
lcNode->right = node;
// 结点的位置变了, 要更新结点的高度值。而lcNode->right得高度没有变化,故不必更新
//顺序:从低到高依次更新。这里,从node到lcNode依次更新。
node->height = max(node->left->getHeight(), node->right->getHeight())+1;
lcNode->height = max(lcNode->left->getHeight(), lcNode->right->getHeight())+1;
node = lcNode;
}
/********************************************************************
singleRotateLeft的图示:
node rcNode
\ / \
rcNode ==> node newAVLNode
/ \ \
rcNode->left newAVLNode rcNode->left
【思想】newAVLNode与rcNode、node在一条线上,并在右子树,故采用单左旋以调整高度使之达到平衡。实际中,如果AVL树在node点不平衡的话,rcNode->left肯定为NULL。
*********************************************************************/
void AVLTree::singleRotateLeft(AVLTreeNode *&node)
{
//RC(right child):rcNode
AVLTreeNode *rcNode = node->right;
node->right = rcNode->left;
rcNode->left = node;
// 结点的位置变了, 要更新结点的高度值。而rcNode->left得高度没有变化,故不必更新
//顺序:从低到高依次更新。这里,从node到rcNode依次更新。
node->height = max(node->left->getHeight(), node->right->getHeight())+1;
rcNode->height = max(rcNode->left->getHeight(), rcNode->right->getHeight())+1;
node = rcNode;
}
/***************************************************************************************************************
doubleRotateRight的图示:
node node newAVLNode
/ / / \
lcNode singleRotateLeft(node->left): newAVLNode singleRotateRight(node) lcNode node
/ \ => / \ => / \ /
lcNode->left newAVLNode lcNode newAVLNode->right lcNode->left newAVLNode newAVLNode
/ \ / \ ->left ->right
newAVLNode->left newAVLNode->right lcNode->left newAVLNode->left
【思想】newAVLNode与lcNode、node在不在一条线上,但同在左子树上,故采用双右旋以调整高度使之达到平衡,即先对lcNode进行单左旋,然后再对node进行单右旋。实际中,如果AVL树在node点不平衡的话,lcNode->left、newAVLNode->left、newAVLNode->right肯定为NULL。
****************************************************************************************************************/
void AVLTree::doubleRotateRight(AVLTreeNode *&node)
{
//先对左结点单左旋,然后再对该结点单右旋
singleRotateLeft(node->left);
singleRotateRight(node);
}
/***************************************************************************************************************
doubleRotateLeft的图示:
node node newAVLNode
\ singleRotateRight \ singleRotateLeft / \
rcNode (node->right): newAVLNode (node): node rcNode
/ \ => / \ => \ / \
newAVLNode lcNode->right newAVLNode rcNode newAVLNode newAVLNode lcNode
/ \ ->left / \ ->left ->right ->right
newAVLNode newAVLNode newAVLNode lcNode->right
->left ->right ->right
【思想】newAVLNode与rcNode、node不在一条线上,但同在右子树,故采用双左旋以调整高度使之达到平衡,即先对rcNode进行单右旋,然后再对node进行单左旋。实际中,如果AVL树在node点不平衡的话,lcNode->right、newAVLNode->left、newAVLNode->right肯定为NULL。
****************************************************************************************************************/
void AVLTree::doubleRotateLeft(AVLTreeNode *&node)
{
//先对右结点单右旋,然后再对该结点单左旋
singleRotateRight(node->right);
singleRotateLeft(node);
}
void AVLTree::_insertAVLNode(AVLTreeNode *&node, AVLTreeNode *newNode)
{
if(NULL == node)
{
node = newNode;
return;
}
if(newNode->data < node->data)//插入到左子树中
{
_insertAVLNode(node->left, newNode);
//以下为AVL树区别于BST树的地方
if(node->left->getHeight()-node->right->getHeight() == 2)//插入后检查是否要进行平衡更新。
{
if(newNode->data < node->left->data)
singleRotateRight(node);//插入到node的左孩子LC的左子树中, 做单右旋
else
doubleRotateRight(node);//插入到node的左孩子LC的右子树中, 做双右旋
}//左子树插入时要右旋,只有这样才能重新平衡AVL树
}
else //插入到右子树中
{
_insertAVLNode(node->right, newNode);
//以下为AVL树区别于BST树的地方
if(node->right->getHeight()-node->left->getHeight() == 2)//AVL树不平衡
{
if (newNode->data >= node->left->data)
singleRotateLeft(node);//插入到右子树右边, 做单左旋
else
doubleRotateLeft(node);//插入到右子树左边, 做双左旋
}//右子树插入时要左旋,只有这样才能重新平衡AVL树
}
node->height = max(node->left->getHeight(), node->right->getHeight())+1;
//从被插入结点一定是叶子结点。每次递归都要计算当前结点的高度,一直到根结点root为止,因为每次插入都会影响到参加旋
//转的结点(包括newAVLNode)及其上面的结点。
}
void AVLTree::insertAVLNode(const int item)
{
AVLTreeNode *newNode = new AVLTreeNode(item);
_insertAVLNode(root, newNode);
}
int main()
{
int a[10] = {9,8,7,6,5,4,3,2,1,0};
AVLTree avlTree;
//二叉搜索树的构建。事实上,二叉搜索树可以实现了数组的排序(只需通过LNR遍历将各结点的数据分别赋回给原数组便可以
//达到排序的效果)
for(int i=0; i!=10; ++i)
avlTree.insertAVLNode(a[i]);
avlTree.printAVLTree();
}