Treap总结(未完工)

Posted mcggvc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Treap总结(未完工)相关的知识,希望对你有一定的参考价值。

(Treap = Tree + Heap)

树堆(Treap),在数据结构中也称Treap,是指有一个随机附加域满足堆的性质的二叉搜索树,其结构相当于以随机数据插入的二叉搜索树。其基本操作的期望时间复杂度为O(logn)。相对于其他的平衡二叉搜索树,Treap的特点是实现简单,且能基本实现随机平衡的结构。
                                                                                        ----百度百科

要了解Treap,就先要看看什么是二叉搜索树

二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

Treap既有BST的性质,也有堆的性质,Treap的每个结点额外附加一个随机值(优先级),让他们按照关键码构成BST的同时也满足堆的性质(父节点优先级高于或低于子节点优先级),因为优先级是随机的,这样在绝大多数情况下我们得到的树都是平衡的,不容易被卡成链, 单次操作期望时间复杂度为O(logn)。

操作:

1.定义

struct Treap {
    int dat, val; 
    int cnt, size, l, r;
} e[N];

dat 随机优先级; val BST关键码; cnt 当前结点数量; size 子树结点数量; l,r 左右儿子。
2.更新

void update(int p) {
    e[p].size = e[e[p].l].size + e[e[p].r].size + e[p].cnt;
}

更新结点size值。
3.新建结点

int newpoint(int val) {
    e[++tot].val = val;
    e[tot].dat = rand();
    e[tot].cnt = e[tot].size = 1;
    return tot;
}

4.建树

void build() {
    newpoint(-inf); newpoint(inf);
    root = 1; e[root].r = 2; update(root);
}

初始化两个结点,设置为无限大和无限小。
5.左右旋转
左右旋转是在保证BST性质前提下(中序遍历不变)将一个结点向上或者向下转,实质上是父节点变成了子节点,子节点变成了父节点。
左旋和右旋分别对应着将左儿子翻上来和将右儿子翻上来。
技术图片
具体操作

void zig(int &p) {//左旋
    int q = e[p].l;
    e[p].l = e[q].r; e[q].r = p; p = q;
    update(e[p].r); update(p);
}
void zag(int &p) {//右旋
    int q = e[p].r;
    e[p].r = e[q].l; e[q].l = p; p = q;
    update(e[p].l); update(p);
}
//p引用的是旋转前的根结点

6.插入
插入一个值为val的结点,若存在关键码为val的结点,将此结点数量 + 1,若不存在,根据val大小和当前结点val大小向左右两边找,最后新建结点,返回时要维护堆性质。

void insert(int &p, int val) {
    if(p == 0) {
        p = newpoint(val); return ;
    }
    if(e[p].val == val) {
        e[p].cnt++; update(p);
        return ;
    }
    if(val < e[p].val) {
        insert(e[p].l, val);
        if(e[p].dat < e[e[p].l].dat) zig(p);
    } else {
        insert(e[p].r, val);
        if(e[p].dat < e[e[p].r].dat) zag(p);
    }
    update(p);
}

7.删除结点
删除一个值为法val的结点,先在树中查找值为val的结点:
1.若结点cnt(数量) 大于1,则可以直接减1,返回;
2.若结点数量为1,将其向下旋转,转到叶结点时直接删除;
注意:在向下旋转时应维护堆的性质,若是大根队,选取子节点中dat大的结点与其交换。

void delet(int &p, int val) {
    if(p == 0) return ;
    if(val == e[p].val) {
        if(e[p].cnt > 1) {
            e[p].cnt--; update(p); return ;
        } else {
            if(e[p].l || e[p].r) {//不为叶结点
                if(e[e[p].l].dat > e[e[p].r].dat || !e[p].r) 
                    zig(p), delet(e[p].r, val);
                else 
                    zag(p), delet(e[p].l, val);
                update(p);
            } else p = 0;
        }//else
        return ;
    }
    if(val < e[p].val) delet(e[p].l, val);  
    else delet(e[p].r, val);
    update(p);
}

以上是关于Treap总结(未完工)的主要内容,如果未能解决你的问题,请参考以下文章

非旋Treap总结 : 快过Splay 好用过传统Treap

关于企业级ECIF系统的构建设计(未完工)

[您有新的未分配科技点]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)

Treap基本用法总结

[读书笔记]普林斯顿微积分读本(修订版)-未完工

[总结] 无旋treap