码迷,mamicode.com
首页 > 其他好文 > 详细

Treap总结(未完工)

时间:2020-02-08 10:07:14      阅读:81      评论:0      收藏:0      [点我收藏+]

标签:维护   mfp   size   oid   子节点   insert   查找   节点   新建   

\(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总结(未完工)

标签:维护   mfp   size   oid   子节点   insert   查找   节点   新建   

原文地址:https://www.cnblogs.com/mcggvc/p/12275388.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!