伸展树(指针版本实现排序)

伸展树是大一暑假开始学的呃……

非常有幸在今年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 );
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值