AVL树是带有平衡条件的二叉查找树。左右子树的高度差不超过1,其实就是-1,0,1。
对AVL树的插入会破坏其平衡。我们把必须调整的节点叫做a。a不平衡会由下面4个插入情况导致:
1.对的左儿子的左子树进行插入。
2.对的左儿子的右子树进行插入。
3.对的右儿子的左子树进行插入。
4.对的右儿子的右子树进行插入。
如下图所示。
上图中,1.4对称,2.3也对称,1.4这一种没有折线的不平衡像是在树的外部插入,而2.3像是在树的内部插入,因为可以从折线中形象的看出来。1.4这样的情况可以通过一次单旋转完成,2.3通过单旋不行,不管是向左还是向右都不行,需要先把不平衡的转成直线形的,然后在来一次左旋或者右旋就可以了。
单旋转的树可以抽象成一下图(以单右旋为例),下图中,k2右旋,然后调整相应节点,就平衡了。
代码如下:
Position CAvlTree::singleRotateToRight(Position k2)// k2将右旋
{
if ( NULL == k2 )
{
return NULL;
}
Position k1 = NULL;
k1 = k2->lchild;
k2->lchild = k1->rchild;
k1->rchild = k2;
if ( CAvlNode *pCAvlNode = (CAvlNode*)(k1) )
{
pCAvlNode->iHeight = height(pCAvlNode);
}
if ( CAvlNode *pCAvlNode1 = (CAvlNode*)(k2) )
{
pCAvlNode1->iHeight = height(pCAvlNode1);
}
return k1;
}
Position CAvlTree::singleRotateToLeft(Position k2)// k2将左旋
{
if ( NULL == k2 )
{
return NULL;
}
Position k1 = NULL;
k1 = k2->rchild;
k2->rchild = k1->lchild;
k1->lchild = k2;
if ( CAvlNode *pCAvlNode = (CAvlNode*)(k1) )
{
pCAvlNode->iHeight = height(pCAvlNode);
}
if ( CAvlNode *pCAvlNode1 = (CAvlNode*)(k2) )
{
pCAvlNode1->iHeight = height(pCAvlNode1);
}
return k1;
}
对于2.3情况,单旋不能消除不平衡,只能两次旋转,第一次讲不平和节点旋转成为直线,然后再进行一次单旋,可以抽象成如下(左-右旋转为例),下图中,先将不平衡节点k3的做孩子k1进行左旋,然后再对k3进行右旋。如下图:
代码如下:
Position CAvlTree::doblueRotateToRight( Position k3 )
{
k3->lchild = singleRotateToLeft(k3->lchild); //对k3的左子树左旋
return singleRotateToRight(k3); //对k3右旋
}
Position CAvlTree::doblueRotateToLeft( Position k3 )
{
k3->rchild = singleRotateToRight(k3->rchild); /对k3的右子树右旋
return singleRotateToLeft(k3); //对k3左旋
}
下面是数据结构:
public:
CTreeNode(int iElement)
{
idata = iElement;
lchild = NULL;
rchild = NULL;
uiTimes = 1;
}
int idata;
CTreeNode *lchild;
CTreeNode *rchild;
unsigned int uiTimes;
}TreeNode,*Position,*Root,*BinarySearchTree;
class CAvlNode : public CTreeNode{
public:
CAvlNode(int iElement)
:CTreeNode(iElement)
{
iHeight = 0;
}
int iHeight;
};
下面是工具函数,第一个是计算一个节点的高度,第二个是计算一个节点的左右子树的高度差。
int CAvlTree::height(TreeNode* pNode)
{
if ( NULL == pNode )//空树的高度为-1
{
return -1;
}else
{
return std::max( height( pNode->lchild ),height( pNode->rchild) ) + 1; //选取左右子树的最高的再加1就是这个节点的高度;
}
}
int CAvlTree::altitudeDifference( TreeNode* pNode )
{
if ( NULL == pNode )//空树处理
{
return -1;
}
return std::abs( height(pNode->lchild) - height(pNode->rchild) );
}
有了以上的函数,就可进行插入操作了,采用的是递归算法:
Root CAvlTree::insertNode( int iElement,Root pRoot )
{
if ( NULL == pRoot )
{
pRoot = new CAvlNode(iElement);
}else if ( iElement < pRoot->idata )
{
pRoot->lchild = insertNode(iElement,pRoot->lchild);
if ( altitudeDifference(pRoot) == 2 )
{
if ( iElement < pRoot->lchild->idata ) //这里为何不用pRoot->rchild?因为pRoot->rchild有可能是空,下面以此类推。
{
pRoot = singleRotateToRight(pRoot); //直线型
}else
{
pRoot = doblueRotateToRight(pRoot); //半棱形
}
}
}else if ( iElement > pRoot->idata )
{
pRoot->rchild = insertNode(iElement, pRoot->rchild);
if ( altitudeDifference(pRoot) == 2 )
{
if ( iElement > pRoot->rchild->idata )
{
pRoot = singleRotateToLeft(pRoot);
}else
{
pRoot = doblueRotateToLeft(pRoot);
}
}
}else
{
pRoot->uiTimes++;
}
CAvlNode* node = static_cast<CAvlNode*>(pRoot);
node->iHeight = height(pRoot);
return pRoot;
}
把原理弄清楚了,写代码就很容易了,所以在学习的时候,我一般都是先从原理入手,然后画图,最后写代码验证,跟做设计一个流程。