《树》之伸展树
本文介绍什么?
使用伸展树有什么样的效果;
伸展树的定义;
伸展树ADT具体实现过程的描述;
代码实现。
一、使用伸展树(splay tree)的效果:
使用伸展树时,对伸展树上任意一次操作的最坏运行时间为 O( N );但是,它保证了连续M次操作花费的最多时间为O(M ㏒N),从而可以推算出对伸展树的每一次操作的摊还时间为O( ㏒N)。
二、伸展树的定义:
对于一颗二查查找树进行操作时,每访问一个节点,该节点都通过旋转操作被放到根上。这样的一颗二查查找树称为伸展树。
三、伸展树ADT的描述:
1.伸展树的节点定义与二查查找树节点定义的方法一致,此处不再赘述;
2.比起二查查找树,伸展树ADT只是对了一个旋转操作。在这里的旋转,我们称之为展开(Splaying)。设节点X为访问的节点,我们通过对节点X进行一系列操作,将节点X移动到根节点。
3.节点X旋转的情况:
⑴节点X为根节点:什么都不做;
⑵节点X的父节点为根节点:
根节点的左子树=X的右子树;
X的右子树=根节点;
⑶节点X具有父节点(P)、祖父节点(G),此时有(两类)四种情况需要考虑:
ⅰ、( "一" 字形) P是G的左儿子,X是P的左儿子;
ⅱ、( "一" 字形)P是G的右儿子,X是P的右儿子;
ⅲ、( "之" 字形)P是G的左儿子,X是P的右儿子;
ⅳ、( "之" 字形)P是G的右儿子,X是P的左儿子;
4.节点的删除:
由于对节点X的每一次访问都需要将X移向根节点,所以懒惰删除在这里显得不太合适,在这里需要进行的直接删除。当进行删除操作时,首先需要访问节点X,节点X被移动到根节点,此时删除X花费的代价是比较小的。在删除节点X的时候,①访问X导致节点X被移向根节点,删除节点X并得到左、右两棵子树(TL、TR);②在右子树TR上访问最小节点Y(该节点一定没有左儿子),将Y移动到右子树的根节点;③令Y的左儿子为左子树TL。
四、核心代码实现:
1.伸展树的伸展实现
ⅰ、( "一" 字形) P是G的左儿子,X是P的左儿子;
操作:
G的左儿子=P的右儿子;
P的右儿子=G;
P的左儿子=X的右儿子;
X的右儿子=P;
//一字形(左左)static Position ZigzigLL(Position g){ Position p,x; p=g->left;x=p->left; g->left=p->right; p->right=g; p->left=x->right; x->right=p; return x;}
ⅱ、( "一" 字形)P是G的右儿子,X是P的右儿子;
操作:
G的右儿子=P的左儿子;
P的左儿子=G;
P的右儿子=X的左儿子;
X的左儿子=P;
//一字形(右右)static Position ZigzigRR(Position g){ Position p,x; p=g->right;x=p->right; g->right=p->left; p->left=g; p->right=x->left; x->left=p; return x;}
ⅲ、( "之" 字形)P是G的左儿子,X是P的右儿子;
操作:
G的左儿子=X的右儿子;
X的右儿子=G;
P的右儿子=X的左儿子;
X的左儿子=P;
//一字形(左右)static Position ZigzigLR(Position g){ Position p,x; p=g->left;x=p->right; g->left=x->right; x->right=g; p->right=x->left; x->left=p; return x;}
ⅳ、( "之" 字形)P是G的右儿子,X是P的左儿子;
操作:
G的右儿子=X的左儿子;
X的左儿子=G;
P的左儿子=X的右儿子;
X的右儿子=P;
//一字形(右左)static Position ZigzigRL(Position g){ Position p,x; p=g->right;x=p->left; g->right=x->left; x->left=g; p->left=x->right; x->right=p; return x;}
3.伸展树的删除操作实现
//删除节点void Delete(const ElementType x,const sTree st){ if(NULL==st) printf("该树为空树\n"); else{ position temp=Find(x,st); Position root=FindMin(temp->right); root->left=temp->left; free(temp); temp=NULL; return ; }}