#include "stdio.h" #include "stdlib.h" typedef struct _Node{ int data; int h; struct _Node* lf; struct _Node* rt; }Node, *pNode; int ht(pNode a){ return a?a->h:0; } int mh(pNode a, pNode b){ return ht(a)>ht(b)?ht(a):ht(b); } void LL(pNode* root){ pNode t, w = *root; t = w->lf; w->lf = t->rt; t->rt = w; *root = t; w->h = mh(w->lf, w->rt) +1; //移到了右边,更新高度 } void RR(pNode* root){ pNode t, w = *root; t = w->rt; w->rt = t->lf; t->lf = w; *root = t; w->h = mh(w->lf, w->rt) +1; //移到了左边,更新高度 } void LR(pNode* root){ RR(&((*root)->lf)); //这句后面是否需要加一句更新高度?不需要 LL(root); } void RL(pNode* root){ LL(&((*root)->rt)); RR(root); } void rotate(pNode* root){ //旋转平衡 pNode w = *root; if(ht(w->lf) - ht(w->rt) == 2){ if(ht(w->lf->lf) > ht(w->lf->rt)) LL(root); else LR(root); } if(ht(w->rt) - ht(w->lf) == 2){ if(ht(w->rt->rt) > ht(w->rt->lf)) RR(root); else RL(root); } w = *root; //为什么要这样?因为在前面旋转时,*root已经改变,w和*root已经不是指向同一个地方 w->h = mh(w->lf, w->rt) +1; //更新高度 } void insert(pNode* root, int val){ pNode w = *root; if(!w){ w = (pNode)malloc(sizeof(Node)); w->data = val; w->h = 1; w->lf = w->rt = 0; *root = w; return; } if(val > w->data) insert(&(w->rt), val); else insert(&(w->lf), val); rotate(root); } void remove(pNode* root, int val){ pNode t, w = *root; if(!w) return; if(val == w->data){ if(w->rt == 0){ //如果节点只有左子树,直接删掉,并将其左子树接上 *root = w->lf; free(w); return; } t = w->rt; //否则,用该节点右子树的最左子节点来代替,这是个递归的过程 while(t->lf) t = t->lf; w->data = t->data; remove(&(w->rt), t->data); //用右子树的最左节点来代替后,那还得用右子树的最左节点的右子树的最左节点来代替……递归 }else if(val > w->data) remove(&(w->rt), val); else remove(&(w->lf), val); rotate(root); } void show(pNode root){ if(!root) return; printf("%d ", root->data); show(root->lf); show(root->rt); } void main(){ int i; pNode root = 0; for(i=20; i>=0; i--) insert(&root, i); show(root); printf("\n"); remove(&root, 7); remove(&root, 13); remove(&root, 19); remove(&root, 16); show(root); printf("\n"); }
关于高度更新:
经过LL旋转,A的高度更新了,但B的高度没更新,是否需要再写一句代码更新呢?不需要。因为插入B1后,B1高度变成h,然后回退到B节点,更新高度为h+1,此时以B为根的树还是平衡的,不需要旋转,但继续回退到A时,发现不平衡,才开始执行LL,也就是说,在执行LL操作前,B的高度已经更新为h+1,LL操作后,不需要再更新B的高度。
即:LL操作中,只更新降为右子树的节点(例如上图的A点)即可。不需要再写一句来更新根(B点)的高度。
RR操作与LL操作相同,只需更新降为左子树的节点(如上图的A)即可
LR操作是先RR操作再LL操作。通过上面可知,RR操作时,上图中B的高度更新了,再进行LL操作时,A的高度也更新了,最后C成了树根,高度没更新,那是否也和上面一样不需要再写一句更新呢?答案是需要。
C在其子树插入节点后,树高更新为h,等LR操作结束后,C成了树根,其高度应该为h+1,所以需要再写一句更新高度。
RL操作与LR操作同理,过程中会先更新B的高度,再更新A的高度,最后需要再写一句来更新C的高度
更多参考:
http://www.asiteof.me/2010/06/avl/
http://www.asiteof.me/2010/06/avl/
- - - - - - - - - - - - - - - - -- - - - - - - -- - - - - - - - - - - - - -- -- - -- - - - - - - - - - -
进一步浓缩:
#include "stdio.h" #include "stdlib.h" typedef struct _Node{ int val; int h; struct _Node* ch[2]; }Node, *pNode; int ht(pNode r){ return r?r->h:0; } void update(pNode r){ r->h = (ht(r->ch[0])>ht(r->ch[1])?ht(r->ch[0]):ht(r->ch[1])) + 1; } void xxx(pNode* r, int op){ //0, LL; 1, RR; pNode t, w = *r; t = w->ch[op]; w->ch[op] = t->ch[!op]; t->ch[!op] = w; *r = t; update(w); } void zzz(pNode* r, int op){ //0, LR; 1, RL; xxx(&((*r)->ch[op]), !op); xxx(r, op); } void rotate(pNode* r){ int op; pNode w = *r; for(op=0; op<2; op++){ if(ht(w->ch[op])-ht(w->ch[!op]) == 2){ if(ht(w->ch[op]->ch[op]) > ht(w->ch[op]->ch[!op])) xxx(r, op); else zzz(r, op); } } update(*r); } void insert(pNode* r, int val){ pNode w = *r; if(!w){ w = (pNode)malloc(sizeof(Node)); w->val = val; w->h = 1; w->ch[0] = w->ch[1] = 0; *r = w; return; } if(val > w->val) insert(&(w->ch[1]), val); else insert(&(w->ch[0]), val); rotate(r); } void del(pNode* r, int val){ pNode t, w = *r; if(!w) return; if(val == w->val){ if(!(w->ch[1])){ *r = w->ch[0]; return; } t = w->ch[1]; while(t->ch[0]) t = t->ch[0]; w->val = t->val; del(&(w->ch[1]), t->val); }else if(val > w->val) del(&(w->ch[1]), val); else del(&(w->ch[0]), val); update(w); } void show(pNode r){ if(!r) return; printf("%d ", r->val); show(r->ch[0]); show(r->ch[1]); } void destroy(pNode r){ if(!r) return; destroy(r->ch[0]); destroy(r->ch[1]); free(r); } void main(){ int i; pNode root = 0; for(i=0; i<20; i++) insert(&root, i); show(root); printf("\n"); del(&root, 3); del(&root, 7); del(&root, 10); show(root); printf("\n"); }