【娱乐】splay排序

splay做排序

2018将至,这也就当娱乐吧

洛谷上有一篇splay版的,但那份题解中的代码太长(4.3k),这里只有1.4k

其实其它题解都挺简单易懂的
但是本蒟蒻发现对于排序,二叉排序树中就带着“排序”俩字,为啥不用它呢

(实践证明虽然打不过STL,但在其它的算法中还挺快的,总共152ms,加上随机化会更快)

使用这种二叉树就有一点二分的感觉
——用一棵二叉树去找一个元素在一个已经排好序的序列中的位置
并且找到位置后只用加上一个儿子节点即可,不用一个序列从前往后整体移动(花销很大)

↑这里将时间复杂度从 O(n) O ( n ) 降到了 O(log2n) O ( l o g 2 n )

但直接在一棵二叉排序树中插入节点有可能退化为一条链的情况(数据刚好从大到小,时间复杂度化为 O(n2) O ( n 2 ) )
所以加上维护排序树平衡的操作(就是splay中的左旋右旋)

左旋右旋就是在保证整棵树的中序遍历不变的前提下改变这棵树的构造,具体可以百度(写在洛谷里面可能有点占空间)

左右旋转之后基本能保证插入时间复杂度平均在 O(nlog2n) O ( n l o g 2 n ) 左右

下面贴上自以为很短的代码(比4.3k版本小了许多):

#include<bits/stdc++.h>
using namespace std;
#define rg register
#define blood(x) (ch[f[x]][1]==x)

template <typename _Tp> inline void read(_Tp&x){
    char c11=getchar();x=0;bool booo=0;
    while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-'){c11=getchar();booo=1;}
    while(isdigit(c11)){x=x*10+c11-'0';c11=getchar();}if(booo)x=-x;return ;
}

const int N=105000;
int data[N],f[N],ch[N][2];
int root,sz,n;

void init();

inline void rotate(int x){
    //左旋与右旋   这里其实将左右旋合并了,使代码简短了很多
    int fa=f[x],grand=f[f[x]],le=blood(x);
    f[x]=grand;
    if(grand)ch[grand][blood(fa)]=x;
    ch[fa][le]=ch[x][le^1];
    if(ch[fa][le])f[ch[fa][le]]=fa;
    f[fa]=x;ch[x][le^1]=fa;
    return ;
}

void splay(int x,int target){
    for(rg int fa;(fa=f[x])!=target;rotate(x))
        if(f[f[x]]!=target)
            rotate(blood(x)==blood(f[x])?f[x]:x);
    if(!target)root=x;
    return ;
}

void ins(int x){
    if(!root){root=++sz;ch[sz][0]=ch[sz][1]=f[sz]=0,data[sz]=x;return ;}
    int now=root;
    while("Happy New Year!!!")
        if(ch[now][data[now]<x])
            now=ch[now][data[now]<x];
        else break;
    ch[now][data[now]<x]=++sz;
    f[sz]=now,data[sz]=x;
    ch[sz][0]=ch[sz][1]=0;
    splay(sz,0);
    return ;
}

void print(int now){//输出
    if(ch[now][0])print(ch[now][0]);
    printf("%d ",data[now]);
    if(ch[now][1])print(ch[now][1]);
    return ;
}

int main(){
    init();
    print(root);
    return 0;
}

void init(){read(n);int A;for(rg int i=1;i<=n;++i){read(A);ins(A);}return ;}//读入
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值