一、概念
平衡二叉树(AVL树)
特点:左子树和右子树都是平衡二叉树,且左右子树的深度差绝对值不超过1。
平衡因子BF:定义为该节点的左子树深度减去它的右子树深度。则平衡二叉树上的所有结点的平衡因子只可能是-1,0,1.只要二叉树上有一个结点的平衡因子的绝对值大于1,那么该二叉树就是不平衡的。
举例:
下图1不是BST树,59>58
下图2不是,58不满足左右子树高度差为1的条件
下图3不是
二、实现
有以下一堆数字,实现依次创建为平衡二叉树:3 2 1 4 5 6 7 8 10 9,从而理解左旋和右旋。
插入“1”后发现“3”不平衡(3的左子树-右子树个数=2>1),需要将“3”右旋,如下:
也就是说,插入A节点之后,B不平衡,就要旋转B节点。那么旋转的方向,取决于那哪边平衡因子小,上例中,左节点平衡因子大于右节点的,就往右旋转。
什么叫向右旋?就是将不平衡的B旋转到节点的右边
然后插入“4”,“5”,插入完“5”后,会发现“3”和“2”不平衡,他们的平衡因子为2,因此对“3”左旋:
此时“2”和“3”都平衡了,然后继续插入“6”,发现“2”不平衡了,需要对“2”进行右旋:
1.先让“2”的右子树变为“3”
2.让“2”的整体变为“4”的左子树
然后插入“7”,发现“5”不平衡,对“5”左旋:
插入"10"和“9”
此时“7”不平衡,右左旋,先把“10“放在“9”的右子树,然后把“7”放在“9”的左子树,把根“9”和前面的树根“6“连在一起。
C++代码实现
class Node{
Node():m_left(NULL),m_right(NULL),m_bf(0){}
Node(int v):m_value(v),m_left(NULL),m_right(NULL),m_bf(0){}
int m_value;//值
Node* m_left;//左孩子
Node* m_right;//右孩子
int m_bf;//平衡因子
};
class AVLTree
{
public:
AVLTree():m_root(NULL){}
void InsertAVLTreeValue(int v)
{
InsertAVLTreeValue(root,v);
}
void InsertAVLTreeValue(Node*& root,int v);
void InOrder()
{
InOrder(root);
}
void InOrder(Node* root);
void RotateR(Node* root);
void RotateL(Node* root);
void RotateRL(Node* root);
void RotateLR(Node* root);
private:
Node* m_root;//指向根节点的指针
};
void AVLTree::RotateL(Node* root)
{
Node* child=root;
root=child->m_right;
child->m_right=root->left;
root->left=root;
root->m_bf=child->m_bf=0;
}
void AVLTree::RotateR(Node* root)
{
Node* child=root;
root=child->m_right;
child->m_left=root->right;
root->right=root;
root->m_bf=child->m_bf=0;
}
void AVLTree::RotateLR(Node* root)
{
Node* childR=root;
Node* childL=childR->m_right;
root=childL->m_right;
childL->m_right=root->m_left;
root->m_left=childL;
if(root->m_bf<=0)
childL->m_bf=0;
else
child->m_bf=-1;
childR->m_left=root->m_right;
root->m_right=childR;
if(root->m_bf==-1)
childR->m_bf=1;
else
childR->m_bf=0;
root->m_bf=0;
}
void AVLTree::RotateRL(Node* root)
{
Node* childL=root;
Node* childR=childL->m_left;
root=childR->m_left;
childR->m_left=root->m_right;
root->m_right=childR;
if(root->m_bf>=0)
childL->m_bf=0;
else
child->m_bf=1;
childR->m_right=root->m_left;
root->m_left=childL;
if(root->m_bf==1)
childL->m_bf=-1;
else
childL->m_bf=0;
root->m_bf=0;
}
void AVLTree::InOrder(Node* root)
{
if(root!=NULL)
{
InOrder(root->m_left);
cout<<root->m_value<<" ";
InOrder(root->m_right);
}
}
void AVLTree::InsertAVLTreeValue(Node*& root,int v)
{
Node* p=root;
Node* parent=NULL;
stack<Node*> ss;//用栈存放待插入新结点路径上的结点,以便后续计算平衡因子bf
//1.标记parent和p并存放路径的节点到栈内
while(p!=NULL)
{
if(p->m_value==v)//如果待插入结点和树内已存在结点相同就不插入了
return;
parent=p;
aa.push(parent);
if(v<parent->value)
p=p->m_left;
else
p=p->m_right;
}
p=new Node(v);//2.创建新节点存放值
if(parent==NULL)//树为空当前节点为根节点
{
root=p;
return ;
}
//3.把新结点和前面已经创建完的树链接到一起
if(v<parent->m_value)
parent->m_left=p;
else
parent->m_right=p;
//4.通过旋转使其满足AVL树条件
while(!ss.empty())
{
parent=ss.top();//栈顶元素赋值给parent
ss.pop();//出栈
if(parent->m_left==p)
parent->m_bf--;//左--
else
parent->m_bf++;//右++
if(parent->m_bf==0)//★平衡就不用再看了
break;
if(parent->m_bf==1||parent->m_bf==-1)
{//此时需要看下上一层结点,循环执行,如果栈不空就从栈内继续取数据
p=parent;
}
else//bf==2,需要旋转
{
/*旋转分为两大类
*1.单旋转:当parent和p的平衡因子是同号
*2.双旋转:异号
*/
int flag=parent->m_bf>0?1:-1;
if(p->m_bf==flag)//同号
{
if(flag==-1)
RotateR(parent);//右旋
else
PotateL(parent);//左旋
}
else{//异号
if(flag==1)
RotateRL(parent);//右左旋
else
RotateLR(parent);//左右旋
}
}
//5.旋转完之后和原来的树链接到一起
if(ss.empty())
{
m_root=parent;
}
else{
Node* q=ss.top();
ss.pop();
if(q->m_value > parent->m_value)
q-<m_left=parent;
else
q->m_right=parent;
}
}
}
void main()
{
int num[]={3,2,1,6,5,4,7,10,9,8};
int n=sizeof(num)/sizeof(num[0]);
AVLTree t;
for(int i=0;i<n;i++)
{
t.InsertAVLTreeValue(num[i]);
}
t.InOrder();
cout<<endl;
}