ACM算法总结 数据结构(二)(树)

本文深入探讨了树形数据结构的多种类型及其关键特性,包括二叉搜索树、堆、Treap、Splay和LCT等。每种结构都详细讲解了其支持的操作、应用场景及模板代码,为读者提供了全面的树形数据结构知识。


一般我们考虑的树都是无向树,即联通非循环图(也就是联通无向图中没有环),如果为非联通非循环图,我们称之为森林

而在树形结构中,二叉树具有很多很好的性质,也有很多有用的应用,所以一般都以二叉树为基础进行讨论。




二叉搜索树

一种中序遍历为原序列的树形结构,对于每一个子树,左儿子比自己小,右儿子比自己大,如果必要的话,可以给每个结点加权,记录每个数出现次数。

一个二叉搜索树:

基于这种结构,二叉搜索树支持以下功能:

  • 插入:从根节点开始不断比较,小往左,大往右,最后放到合适位置;
  • 删除:对于叶子结点直接删除;对于有一个儿子的结点,删除后把儿子放到该结点位置;否则,寻找右子树中最小的值(即以右儿子为根,不断往左寻找)进行替换;
  • 查找:这个就很简单了;
  • 查询排名:带有 size 的深度搜索;
  • 前继后继:跟查找差不多;




堆是一种完全二叉树,分为大根堆小根堆,大根堆即所有子树的根节点都比儿子要大,小根堆同理。

一个大根堆:

基于这种数据结构,堆支持以下功能:

  • 查询最值:即堆顶;
  • 插入:我们将新的值放在最底层最右边,然后不停向上更新,直到新插入的值小于父亲;
  • 弹出:将最后的值放到堆顶,然后不停向下更新更大的儿子,直到该值大于所有儿子;




Treap

尽管对于随机数来说二叉搜索树非常高效,但是在一些特殊情况下,它可能会退化成一条较长的链。所以,随机平衡二叉搜索树——Treap,用于解决平衡问题。Treap=Tree+Heap,Treap结合了二叉搜索树和堆的性质,为每一个结点分配一个随机数,在满足二叉搜索树的性质的前提下,对随机数满足堆的性质。这样由于随机数是随机的,所以二叉搜索树是平衡的。

Treap的核心操作是旋转,从以下例子可以看出,不管是左旋还是右旋,都不会影响Treap的二叉搜索树的性质:

我们可以看出,旋转的规律就是:把当前结点的反儿子(左旋右儿子,右旋左儿子)的正儿子(左旋左儿子,右旋右儿子)拿出来,然后把待旋转的两个结点对应移一下,再把拿出来的结点放到原来结点的反方向。

由于考虑到Treap要满足堆的性质,所以我们在插入、删除结点的时候,还要考虑满足大根堆或者小根堆,这通过旋转来实现。插入和删除的操作跟普通二叉搜索树差不多,只是对于插入操作,某个结点插入了新的儿子时,要对随机数数组 rd 进行 rotate(旋转)操作,以满足堆性质;对于删除操作,可以将待删除的结点 rotate 到叶子结点处,这样方便处理,中途也要考虑满足堆的性质。

其它的功能,凡是二叉搜索树能满足和实现的,Treap也能同样地实现。

Treap模板如下(普通平衡树):

// t为每个结点的值;num为每个值出现次数;ch为儿子指针;rd为结点随机数;siz为结点子树大小
// root记录树根,tot记录结点个数

struct treap
{
   
   
    int tot,root;
    int *t,*num,(*ch)[2],*rd,*siz;

    treap(int maxn)
    {
   
   
        t=new int[maxn](); num=new int[maxn]();
        rd=new int[maxn](); siz=new int[maxn]();
        ch=new int[maxn][2](); root=tot=0;
    }

    void push_up(int k) {
   
   siz[k]=siz[ch[k][0]]+siz[ch[k][1]]+num[k];}

    void rotate(int &k,int d)
    {
   
   
        int x=ch[k][d^1];
        ch[k][d^1]=ch[x][d]; ch[x][d]=k;
        push_up(k); push_up(x);
        k=x;
    }

    void insert(int &k,int x)
    {
   
   
        if(!k) {
   
   k=++tot; t[k]=x; num[k]=siz[k]=1; rd[k]=rand(); return;}
        else if(t[k]==x) {
   
   num[k]++; siz[k]++; return;}
        else
        {
   
   
            int d=x>t[k];
            insert(ch[k][d],x);
            if(rd[k]<rd[ch[k][d]]) rotate(k,d^1);
            push_up(k);
        }
    }
    void insert(int x) {
   
   insert(root,x);}

    void del(int &k,int x)
    {
   
   
        if(!k) return;
        if(x!=t[k]) del(ch[k][x>t[k]],x);
        else
        {
   
   
            if(!(ch[k][0]|ch[k][1])) {
   
   num[k]--; siz[k]--; if(!num[k]) k=0;}
            else if(!ch[k][0] && ch[k][1]) {
   
   rotate(k,0); del(ch[k][0],x);}
            else if(ch[k][0] && !ch[k][1]) {
   
   rotate(k,1); del(ch[k][1],x);}
            else {
   
   int d=rd[ch[k][0]]>rd[ch[k][1]]; rotate(k,d); del(ch[k][d],x);}
        }
        push_up(k);
    }
    void del(int x) {
   
   del(root,x);}

    int ranking(int k,int x)
    {
   
   
        if(!k) return 0;
        if(x==t[k]) return siz[ch[k][0]]+1;
        if(x<t[k]) return ranking(ch[k][0],x);
        return ranking(ch[k][1],x)+siz[ch[k][0]]+num
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值