《树》之AVL树
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《树》之AVL树相关的知识,希望对你有一定的参考价值。
本文简介:
AVL树的简单介绍
AVL树几种实现方法的对比
AVL树删除节点的分析
AVL树插入节点的分析
AVL树ADT的简单实现
一、简介
定义: AVL(Adlson-Velskii和Landis)树是带有平衡条件的二查查找树。对于AVL树来说,其平衡条件有两点限制:
⑴易于保持;
⑵保证树的深度为O(log N)。
二、AVL树几种实现方法的对比
几种平衡条件的讨论
⑴树上每个节点都必须要有相同高度的左子树和右子树;
在该平衡条件下,记根节点的高度为0,则高度为K的AVL树,其节点个数为2^k-1;此时该AVL树也是一颗满二查树。虽然该条件实现了AVL树,但是过于严格,难以满足普遍数据,所以在这里不采用。
⑵树上每个节点的左子树高度与右子树高度之差的绝对值不大于1;
满足该平衡条件时,节点左、右子树的高度差的绝对值有0,1两种情况。
①当所有节点的左、右子树的高度相等时,该AVL树的节点个数最大,为2^k-1;
②当所有非叶节点的左、右子树的高度差的绝对值为1时,该AVL树的节点个数最小,数量可以用递推关系式:S(h)=S(h-1)+S(h-2)+1 (h>=2)得到;其中,h=0时,S(h)=1;h=1时,S(h)=2。此函数与斐波那契数列密切相关。
三、AVL树的节点删除
AVL树需要保持其平衡条件,所以对于可能破坏AVL树平衡条件的操作,需要特别的注意。在对AVL树上的元素进行删除时,可能会破坏树上某些节点的平衡;被破坏的节点可以通过简单的修正来实现,这种修正被称作旋转(rotation)。基于AVL树可以保存大量数据的特性,在删除操作较少的时候,执行懒惰删除更加方便,在这里只考虑懒惰删除。
四、AVL树的节点插入
插入节点时,有四种操作会破坏平衡条件。假设插入新节点后,树中的 α 节点必须被重新平衡。这四种情况分别为:
⑴对 α 的左儿子的左子树进行一次插入;
⑵对 α 的右儿子的右子树进行一次插入;
⑶对 α 的左儿子的右子树进行一次插入;
⑷对 α 的右儿子的左子树进行一次插入;
其中前两种情况为一类,通过进行一次单旋转即可重新实现平衡;后两种情况为另一类,通过进行一次双旋转即可重新实现平衡。
五、AVL树ADT的简单实现
AVL树的ADT声明
#ifndef AVLTree_H #define AVLTree_H typedef int ElementType; struct avLNode; typedef struct avLNode *Position; typedef struct avLNode *avLTree; avLTree MakeEmpty(avLTree avlt); //构造空树 Position Find(const ElementType x,const avLTree avlt); //查找值为x的节点 Position FindMin(const avLTree avlt); //查找AVL树上的最小元素节点 Position FindMax(const avLTree avlt); //查找AVL树上的最大元素节点 Position Insert(const ElementType x,avLTree avlt); //插入值为x的节点 avLTree Delete(ELementType x,avltree avlt); //删除值为x的节点 ElementType reTrieve(const ElementType x,Position p); //把位置p处的值改为x int GetHeight(const Position p); //获得位置p处的节点高度 #endif /*AVLTree_H AVL树ADT的声明*/ //定义树节点 struct avLNode{ ElementType data; avLTree left; avLTree right; int height; };
构造空树算法、三种查找算法
AVL树是二查查找树的一种变形,所以对二查查找树的构造空树操作、查找操作同样适应于AVL树,所以这里不再赘述。
获得高度算法
因为节点的高度不可能小于0,所以,用-1来表示空节点和已删除的节点。
int GetHeight(const Position p) { if(p==null) return -1; else return p->height; }
3.插入值为x的节点,算法的详细步骤分析在上面给出。
Position Insert(const ElementType x,avLTree avlt) { if(NULL==avlt){ int lh=0,rh=0; avlt=new struct avLNode; if(NULL==avlt) ; else { avlt->data=x;avlt->height=-1; avlt->left=avlt->right=NULL; } } else if(x<(avlt->data)){ avlt->left=Insert(x,avlt->left); if(GetHeight(avlt->left)-GetHeight(avlt->right)==2) if(x<(avlt->left->data)) avlt=singleWithLL(avlt);//在左儿子的左子树上插入 else avlt=doubleWithLR(avlt);//在左儿子的右子树上插入 }else if(x>(avlt->data)){ avlt->right=Insert(x,avlt->right); if(GetHeight(avlt->left)-GetHeight(avlt->right)==2) if(x>(avlt->right->data)) avlt=singleWithRR(avlt);//在右儿子的右子树上插入 else avlt=doubleWithRL(valt);//在右儿子的左子树上插入 } lh=GetHeight(valt->left);rh=GetHeight(valt->right); valt->height=lh>rh?lh:rh; return valt; }
4.四种旋转的详细介绍和实现
⑴在 α 的左儿子的左子树上插入(左左型)
static Position singleWithLL(Position k1) { vlTree k2; k2 = k1->left; k1->left = k2->right; k2->right = k1; k1->height=(GetHeight(k1->left)>GetHeight(k1->Right)?GetHeight(k1->left):GetHeight(k1->right)) + 1; k2->height=(GetHeight(k2->left)>k1->height?GetHeight(k2->keft):k1->height)+1; return k2; }
⑵在 α 的右儿子的右子树上插入(右右型)
static Position singleWithRR(Position k1) { vlTree k2; k2=k1->right; k1=k2->left; k2->keft=k1; k1->height=(GetHeight(k1->left)>GetHeight(k1->Right)?GetHeight(k1->left):GetHeight(k1->right)) + 1; k2->height=(GetHeight(k2->right)>k1->height?GetHeight(k2->right):k1->height)+1; return k2; }
⑶在 α 的左子树的右子树上插入(左右型)
static Position doubleRotateWithLR(Position k3) { k3->right=singleWithLL(k3->right); return singleWithRR(k3); }
⑷在 α 的右子树的左子树上插入(右左型)
static Position doubleRotateWithRL(Position k3) { k3->left=singleWithRR(k3->left); return singleWithLL(k3); }
以上是关于《树》之AVL树的主要内容,如果未能解决你的问题,请参考以下文章