伸展树是大一暑假开始学的呃……
非常有幸在今年ACM天津赛区网络赛上最后10分钟我把一道伸展树的题目给Accepted,确保我们学校在该场比赛出线……
如果面试官问我是否会手写平衡树的话,我只能说会伸展树这一种了,而且伸展树似乎是非主流,其插入,查找等操作的均摊复杂度为log(n);优点不用多说,编码复杂度很低。
具体知识点可以查阅相关资料,下面自己写了一个指针版本的伸展树实现排序的代码,不是很简洁,实际上如果不用left,right来定义左右子结点的指针,而用一个size=2的数组来写的话,代码会好看很多。
#include <cstdio>
#include <cstdlib>
/*----- 定义伸展树的结点 -----*/
struct Node{
int key; // 结点权值;
Node *left, *right, *father; // 结点左右子结点,父结点指针;
Node(int key_, Node *father_){ // 构造函数;
key= key_;
father= father_;
left= right= NULL;
}
~Node(){ // 析构函数;
if( father ){
delete father;
father= NULL;
}
if( left ){
delete left;
left= NULL;
}
if( right ){
delete right;
right= NULL;
}
}
};
Node *R= new Node(0, NULL); // 定义一个虚拟结点作为根节点的父亲结点;
/*----- 结点x绕父节点旋转,t=1:右旋,t=0:左旋; -----*/
void rotate(Node *x, int t){
Node *y= x->father;
if( t==1 ){
if( x->right != NULL ){
y->left= x->right;
x->right->father= y;
}
else y->left= NULL;
}
else{
if( x->left != NULL ){
y->right= x->left;
x->left->father= y;
}
else y->right= NULL;
}
if( y->father->left == y ) y->father->left= x;
else y->father->right= x;
x->father= y->father;
if( t==1 ) x->right= y;
else x->left= y;
y->father= x;
}
/*----- splay操作,把结点x旋转到结点fa下; -----*/
void splay(Node *x, Node *fa){
while( x->father != fa ){
Node *y= x->father;
if( y->father == fa ) rotate( x, x == y->left );
else{
Node *z= y->father;
if( y==z->left ){
if( x==y->left ) rotate( y, 1 ), rotate( x, 1 );
else rotate( x, 0 ), rotate( x, 1 );
}
else{
if( x==y->right ) rotate( y, 0 ), rotate( x, 0 );
else rotate( x, 1 ), rotate( x, 0 );
}
}
}
}
/*----- 插入一个新的结点,权值为delta,每次插入完后将其旋转到根,返回根结点的地址 -----*/
Node *Insert(int delta, Node *root){
if( root==NULL ){
root= new Node(delta, R);
return root;
}
Node *fp, *p= root;
while( p ){
fp= p;
if( p->key > delta ) p= p->left;
else p= p->right;
}
p= new Node( delta, fp );
if( fp->key > delta ) fp->left= p;
else fp->right= p;
splay( p, R );
return p;
}
/*----- 返回以root为根的子树的最小权值结点地址 -----*/
Node *Get_min(Node *root){
while( root->left != NULL ){
root= root->left;
}
splay( root, R );
return root;
}
/*----- 删除根结点,考虑是否有左右子结点,如果都有则选右子树最小key结点作为根 -----*/
Node *Delete(Node *root){
if( root->left == NULL && root->right == NULL )
return NULL;
if( root->left == NULL )
root= root->right;
else if( root->right == NULL )
root= root->left;
else if( root->left != NULL && root->right != NULL ){
Node *p= Get_min( root->right );
splay( p, root );
p->left= root->left;
root->left->father= p;
root= p;
}
root->father= R;
return root;
}
int main(){
int a[]={179,2,1,32,54,823,412,369};
int n= sizeof(a) / sizeof(*a);
Node *root= NULL;
for(int i=0; i<n; ++i){
root= Insert( a[i], root );
}
for(int i=0; i<n; ++i){
root= Get_min( root );
printf("%d ", root->key);
root= Delete( root );
}
}