Treap树

Treap树

Treap一词是Tree和Heap的合成词,也就是这是一颗带有堆性质的树,即树堆,Treap树是一种排序二叉树(二叉搜索树、二分检索树 Binary Serach Tree),简称BST,也就是满足value值大小关系是左孩子<根<右孩子,这样就满足了排序二叉树,刚才讲过,Treap树还满足堆的性质,那么它的哪个值满足堆的性质呢,并不是value,而是一个优先级rank值,这个rank值是人为添加的,一般使用一个随机数,这个rank是为了维持二叉树的平衡而设定的,总的说来,对于键值来说,Treap是一棵排序二叉树,对于优先级来说,Treap是一个堆,当然根节点的优先级是最高的

1、Treap树的唯一性

Treap树的重要特性:另每个节点的优先级互不相等,那么整棵树的形态时唯一的,和元素的插入顺序没有关系。

2、Treap树的平衡问题

树的形态依赖于节点的优先级,那么如何配置每个节点的优先级,才能避免二叉树的形态退化成链表?最简单的方法时把每个节点的优先级进行随机赋值,那么生成的Treap树形态也是随机的。这虽然不能保证每次生成的Treap树一定时平衡的,但是期望的插入、删除、查找的复杂度都是O(logn)的。

3、Treap树的数据结构

struct Node{
    int size;  // 以这个点为根的子树的节点的数量
    int rank;  // 优先级
    int key;  // 键值
    Node *son[2];  //son[0]时左儿子,son[1]是右儿子
    bool operator < (const Node &a)const {return rank < a.rank;}
    int cmp(int x)const{
        if(x == key) return -1;
        return x < key? 0 : 1;
    }
    void update(){  //更新size
        size = 1;
        if(son[0] != NULL) size += son[0] -> size;
        if(son[1] != NULL) size += son[1] -> size;
    }
};

4、Treap树的插入

每读入一个新的节点,为它分配一个随机的优先级,插入到树中,在插入时动态调整树的结构,使它仍然是一棵Treap树。

把新节点插入到Treap树的过程有两步

(1)用朴素的插入方法把node按键值的大小插入到合适的子树上去。

(2)给node随机分配一个优先级,如果node的优先级违反了堆的性质,即它的优先级比父节点高,那么让node往上走,替代父节点,最后得到一个新的Treap树。

void insert(Node * &o, int x){  //插入
    if(o == NULL){
        o = new Node();
        o -> son[0] = o -> son[1] = NULL;
        o -> rank = rand();
        o -> key = x;
        o -> size = 1;
    }
    else{
        int d = o -> cmp(x);
        insert(o -> son[d], x);
        o -> update();
        if(o < o -> son[d])
            rotate(o, d^1);
    }
}

5、插入过程中维护堆的过程中用到的旋转

void rotate(Node * &o, int d){  // d = 0 左旋,d = 1右旋
    Node *k = o -> son[d^1];  //d^1 == 1 - d
    o -> son[d^1] = k -> son[d];
    k -> son[d] = o;
    o -> update();
    k -> update();
    o = k;
}

6、寻找第k大的数 O(logn)

int kth(Node* o, int k){
    if(o == NULL || k <= 0 || k > o -> size)
        return -1;
    int s = o -> son[1] == NULL? 0: o -> son[1] -> size;
    if(k == s + 1) return o -> key;
    else if(k <= s) return kth(o -> son[1], k);
    else return kth(o -> son[0], k - s - 1);
}

7、查询某个数的名次 O(logn)

int find(Node* o, int k){
    if(o == NULL) return -1;
    int d = o -> cmp(k);
    if(d == -1)
        return o -> son[1] == NULL? 1: o -> son[1] -> size + 1;
    else if(d == 1) return find(o -> son[d], k);
    else{
        int tmp = find(o -> son[d], k);
        if(tmp == -1) return -1;
        else
            return o -> son[1] == NULL? tmp + 1: tmp + 1 + o -> son[1] -> size;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追寻远方的人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值