定义:称一颗增长二叉树为高度平衡树,当且仅当或由单一外结点组成,或由两个子树形Tl和Tr组成,并且满足:
(1) |h(Tl) - h(Tr)| ≦1,其中h(T)表示树T的高度;
(2) Tl和Tr都是高度平衡树。
由定义可知空树是平衡二叉树。
定义:设T为增长二叉树,q是T之内结点,qL和qR是q的左、右子树,hL和hR分别为qL和qR的高度,q的平衡系数(或称平衡因子)BF(q)定义为hR-hL。对于平衡树中的任一内结点q,则有BF(q)=0, -1或+1。
1 平衡二叉树的结点
相对于二叉查找树,需要增加二叉树的高度这一属性,为的是完成插入和删除过程中对旋转算法。
//AVL树节点信息
template<class T>
class TreeNode
{
public:
TreeNode():lson(NULL),rson(NULL),freq(1),hgt(0){}
T data;//值
int hgt;//以此节点为根的树的高度
unsigned int freq;//频率
TreeNode* lson;//指向左儿子的地址
TreeNode* rson;//指向右儿子的地址
};
2 平衡二叉树类的声明
//AVL树类的属性和方法声明
template<class T>
class AVLTree
{
private:
TreeNode<T>* root;//根节点
void insertpri(TreeNode<T>* &node,T x);//插入
TreeNode<T>* findpri(TreeNode<T>* node,T x);//查找
void insubtree(TreeNode<T>* node);//中序遍历
void Deletepri(TreeNode<T>* &node,T x);//删除
int height(TreeNode<T>* node);//求树的高度
void SingRotateLeft(TreeNode<T>* &k2);//左左情况下的旋转
void SingRotateRight(TreeNode<T>* &k2);//右右情况下的旋转
void DoubleRotateLR(TreeNode<T>* &k3);//左右情况下的旋转
void DoubleRotateRL(TreeNode<T>* &k3);//右左情况下的旋转
int Max(int cmpa,int cmpb);//求最大值
public:
AVLTree():root(NULL){}
void insert(T x);//插入接口
TreeNode<T>* find(T x);//查找接口
void Delete(T x);//删除接口
void traversal();//遍历接口
};
3 旋转的分类
旋转可分为四类:左左、左右、右左和右右;
①左左:图1中,6结点的左子树比右子树高度大2,左子树3结点的左子树高度大于右子树,这种情况称为左左;
②左右:图2中,6结点的左子树比右子树高度大2,左子树3结点的左子树高度小于右子树,这种情况称为左右;
③右左:图3中,2结点的左子树比右子树高度小2,右子树5结点的左子树高度大于右子树,这种情况称为右左;
④右右:图4中,2结点的左子树比右子树高度小2,右子树4结点的左子树高度小于右子树,这种情况称为右右。
从图中可以看出,1和4两种情况是对称的,只需要经过一次旋转就可以完成,我们称之为单旋转;2和3两种情况是对称的,需要经过两次旋转,我们称之为双旋转。
4 单旋转
//左左情况下的旋转
template<class T>
void AVLTree<T>::SingRotateLeft(TreeNode<T>* &k2)
{
TreeNode<T>* k1;
k1=k2->lson;
k2->lson=k1->rson;
k1->rson=k2;
//更新结点K1和K2的高度
k2->hgt=Max(height(k2->lson),height(k2->rson))+1;
k1->hgt=Max(height(k1->lson),k2->hgt)+1;
}
//右右情况下的旋转
template<class T>
void AVLTree<T>::SingRotateRight(TreeNode<T>* &k2)
{
TreeNode<T>* k1;
k1=k2->rson;
k2->rson=k1->lson;
k1->lson=k2;
//更新结点K1和K2的高度
k2->hgt=Max(height(k2->lson),height(k2->rson))+1;
k1->hgt=Max(height(k1->rson),k2->hgt)+1;
}
5 双旋转
//左右情况的旋转
template<class T>
void AVLTree<T>::DoubleRotateLR(TreeNode<T>* &k3)
{
SingRotateRight(k3->lson);
SingRotateLeft(k3);
}
//右左情况的旋转
template<class T>
void AVLTree<T>::DoubleRotateRL(TreeNode<T>* &k3)
{
SingRotateLeft(k3->rson);
SingRotateRight(k3);
}
6 插入
插入的方法和二叉查找树基本一样,区别是,插入完成后需要从插入的节点开始维护一个到根节点的路径,每经过一个节点都要维持树的平衡。维持树的平衡要根据高度差的特点选择不同的旋转算法。
//在以node为根结点的二叉平衡树中插入x
template<class T>
void AVLTree<T>::insertpri(TreeNode<T>* &node,T x)
{
if(node==NULL)//如果结点为空,就在此结点处加入x信息
{
node=new TreeNode<T>();
node->data=x;
return;
}
if(node->data>x)//如果x小于结点的值,就继续在结点的左子树中插入x
{
insertpri(node->lson,x);//递归插入
if((height(node->lson)-height(node->rson))==2)//结点左右子树高度差为2时,结点node失去平衡,需要旋转
if(x < node->lson->data)//x插入在node结点的左子树中
SingRotateLeft(node);//左左
else
DoubleRotateLR(node);//左右
}
else if(node->data<x)//如果x大于结点的值,就继续在结点的右子树中插入x
{
insertpri(node->rson,x);
if((height(node->rson)-height(node->lson))==2)//结点右左子树高度差为2时,结点node失去平衡,需要旋转
if(x>node->rson->data)//x插入在node结点的右子树中
SingRotateRight(node);//右右
else
DoubleRotateRL(node);//右左
}
else ++(node->freq);//如果相等,就把频率加1
node->hgt=Max(height(node->lson),height(node->rson));//更新节点node的高度值
}
//插入接口
template<class T>
void AVLTree<T>::insert(T x)
{
insertpri(root,x);
}
7 查找
//查找
template<class T>
TreeNode<T>* AVLTree<T>::findpri(TreeNode<T>* node,T x)
{
if(node==NULL)//如果节点为空说明没找到,返回NULL
{
return NULL;
}
if(node->data>x)//如果x小于节点的值,就继续在节点的左子树中查找x
{
return findpri(node->lson,x);
}
else if(node->data<x)//如果x大于节点的值,就继续在节点的左子树中查找x
{
return findpri(node->rson,x);
}
else return node;//如果相等,就找到了此节点
}
//查找接口
template<class T>
TreeNode<T>* AVLTree<T>::find(T x)
{
return findpri(root,x);
}
8 删除
删除的方法也和二叉查找树的一致,区别是,删除完成后,需要从删除节点的父亲开始向上维护树的平衡一直到根节点。
//删除
template<class T>
void AVLTree<T>::Deletepri(TreeNode<T>* &node,T x)
{
if(node==NULL) return ;//没有找到值是x的节点
if(x < node->data)
{
Deletepri(node->lson,x);//如果x小于节点的值,就继续在节点的左子树中删除x
if((height(node->rson)-height(node->lson))==2)//结点node右左子树高度差为2时,结点node失去平衡
if(node->rson->lson!=NULL&&(height(node->rson->lson)>height(node->rson->rson)) )
DoubleRotateRL(node);//右左
else
SingRotateRight(node);//右右
}
else if(x > node->data)
{
Deletepri(node->rson,x);//如果x大于节点的值,就继续在节点的右子树中删除x
if((height(node->lson)-height(node->rson))==2)//结点node左右子树高度差为2时,结点node失去平衡
if(node->lson->rson!=NULL&& (height(node->lson->rson)>height(node->lson->lson) ))
DoubleRotateLR(node);//左右
else
SingRotateLeft(node);//左左
}
else//如果相等,此节点就是要删除的节点
{
if(node->lson&&node->rson)//此节点有两个儿子
{
TreeNode<T>* temp=node->rson;//temp指向节点的右儿子
while(temp->lson!=NULL) temp=temp->lson;//找到右子树中值最小的节点
//把右子树中最小节点的值赋值给本节点
node->data=temp->data;
node->freq=temp->freq;
Deletepri(node->rson,temp->data);//删除右子树中最小值的节点
if(2==height(node->lson)-height(node->rson))
{
if(node->lson->rson!=NULL&& (height(node->lson->rson)>height(node->lson->lson) ))
DoubleRotateLR(node);
else
SingRotateLeft(node);
}
}
else//此节点有1个或0个儿子
{
TreeNode<T>* temp=node;
if(node->lson==NULL)//有右儿子或者没有儿子
node=node->rson;
else if(node->rson==NULL)//有左儿子
node=node->lson;
delete(temp);
temp=NULL;
}
}
if(node==NULL) return;
node->hgt=Max(height(node->lson),height(node->rson))+1;
return;
}
//删除接口
template<class T>
void AVLTree<T>::Delete(T x)
{
Deletepri(root,x);
}
9 判断是否为平衡二叉树
求树的深度:
struct BinaryTreeNode
{
int m_Value;
BinaryTreeNode* m_pLeft;
BinaryTreeNode* m_pRight;
};
int TreeDepth(BinaryTreeNode* pRoot)
{
if (pRoot == NULL)
return 0;
int nLeftDepth = TreeDepth(pRoot->m_pLeft);
int nRightDepth = TreeDepth(pRoot->m_pRight);
return (nLeftDepth>nRightDepth)?(nLeftDepth+1):(nRightDepth+1);
}
递归判断是否为平衡二叉树
bool IsBalanced(BinaryTreeNode* pRoot)
{
if(pRoot== NULL) //空树是平衡二叉树
return true;
int nLeftDepth = TreeDepth(pRoot->m_pLeft);
int nRightDepth = TreeDepth(pRoot->m_pRight);
int diff = nRightDepth-nLeftDepth;
if (diff>1 || diff<-1)//左右子树高度差的绝对值要小于等于1
return false;
return IsBalanced(pRoot->m_pLeft)&&IsBalanced(pRoot->m_pRight);
}