AVL树是非常重要的一种数据结构,这里实现了在AVL树中的插入操作,包括插入后整个树的自平衡。
这里有几点值得注意的地方:
1).左旋L_Rotate与右旋R_Rotate操作:
这两个操作传递进来的参数是以TreeNode*&的形式传递进来的,也就是说传递的是指针的引用,效果等价于传递二级指针
如果不加入&,则在函数内部更改的是形参的指向,因为实际上函数调用时,如果不采用引用传递,则会构造一个与原T指向同一个地方的临时变量指针,在X_Rotate的内部也是对这个临时变量进行操作,等到返回后对原来的T一点影响都没有。因此对于指针的操作,如果说需要在某个函数内更改这个指针的指向,则要么传递二级指针,要么传递指针的引用。
2).在LR型或RL型中:
以LR型为例,要根据不平衡点的左子树的根的右孩子rd的bf值来确定T与lc的bf值,其中rd->bf会出现等于0的情况。
这种情况只会出现在rc才是新插入的节点,也就是说lc->right在原来未插入时是NULL,只有在这种情况下才会显现在rc->bf=0,树却增高的情况。
一点小小的总结:
AVL树第一次接触感觉很复杂,转来转去,四个形状,其实思考清楚后整个思路还是很简单的:
首先是LL型与RR型:
这两种情况是最简单的,只需要简单的右旋/左旋即可.
以LL型为例子:对T右旋后,实际上就是将T->left->right接到T的left上,并将T->left->right改为接上T。
RR型也是如此。
然后是LR型与RL型:
这两种情况复杂的原因在于,仅仅是右旋/左旋,T的bf仍不会变化。
以LR型为例子:
问题出现在右旋后将T->left->right接到T的left并不会改变T的bf。
但实际上LR型可以看做这样一个过程:先将他转换为LL型。
也就是说,如果把插入节点插入到T->left的左子树上,就转变为第一个问题了。
现在的问题在于怎么把插入在T->left的右子树上的节点看做插在左子树上呢,只要我们对T->left进行一次左旋。
我们现在将T->left看做一个树的根节点,对这个T’进行一次左旋,根据左旋的规则发现原本插在T'->right上的节点现在被旋转到T'->right上了,而T->left的位置现在被T'->left->right所占据,现在的问题变为了LL型,则只要想问题一一样进行一次右旋就可以了。
这也就是LR型的整个过程。
对RL型也是如此。
struct TreeNode { int val; int bf; struct TreeNode* left; struct TreeNode* right; }; class Solution { public: int InsertAVL(TreeNode*& T,int val,bool& taller) { if(!T) { T=new TreeNode; T->bf=0; T->left=T->right=NULL; T->val=val; taller=true; } else { if(val==T->val) { taller=false; return 0; } else if(val<T->val)//left { if(!InsertAVL(T->left,val,taller)) return 0; if(taller) { switch(T->bf) { case -1: taller=false;T->bf=0;break; case 0: taller=true;T->bf=1;break; case 1: LeftBalance(T);taller=false;break; } } } else { if(!InsertAVL(T->right,val,taller)) return 0; if(taller) { switch(T->bf) { case 1: taller=false;T->bf=0;break; case 0: taller=true;T->bf=-1;break; case -1: RightBalance(T);taller=false;break; } } } } return 1; } private: void R_Rotate(TreeNode*& p)//注意这里是传递的指针的引用,效果等价于传递二级指针 { TreeNode* lc=p->left; p->left=lc->right; lc->right=p; p=lc; } void L_Rotate(TreeNode*& p) { TreeNode* rc=p->right; p->right=rc->left; rc->left=p; p=rc; } void LeftBalance(TreeNode*& T) { TreeNode* lc=T->left; switch(lc->bf) { case 1: T->bf=lc->bf=0;R_Rotate(T);break; case -1: TreeNode* rd=lc->right; switch(rd->bf) { case 1: lc->bf=0;T->bf=-1;break; case 0://注意这里rd->bf=0.这种情况是rd这个节点就是新插入的节点,它的左右子树都为空,所以rd->bf=0.只有这种情况才会出现rd->bf=0 lc->bf=T->bf=0;break; case -1: lc->bf=1;T->bf=0;break; } rd->bf=0; L_Rotate(T->left); R_Rotate(T); break; } } void RightBalance(TreeNode*& T) { TreeNode* rc=T->right; switch(rc->bf) { case -1: rc->bf=T->bf=0;break; case 1: TreeNode* ld=rc->left; switch(ld->bf) { case -1: rc->bf=0;T->bf=1;break; case 0: rc->bf=T->bf=0;break; case 1: rc->bf=-1;T->bf=0;break; } ld->bf=0; R_Rotate(T->right); L_Rotate(T); break; } } };