[平衡树]Tree(BST) + Heap = Treap

引入

首先其实平衡树就是BST,即二叉查找树,然后尽量使这棵BST保持平衡。
我们知道BST容易退化,所以就有人发明了AVL树和红黑树,虽然这两种树的运行速度很快=_=,但是代码很复杂, 插入删除旋转情况有很多种,才保证了它的平衡,所以后来又出现了Treap这种平衡树。
虽然Treap比较简单,但是时间上却要稍稍差一点,而且有极小到几乎没有的可能(但是还是有)会退化,但显然在平时也已经够用了, 所以这里先来看看Treap。

数据结构的储存

首先因为这是一棵二叉查找树,所以每个节点必然要存两个儿子和它本身的值,然后在这个基础上,加上一个值,作为它的优先级,那么优先级有什么用,待会儿再说。

实现原理

通过题目可以知道,Treap就是Tree(BST)+Heap
Heap就是堆,所以一棵Treap肯定首先满足了BST的性质,然后怎么满足Heap的性质呢
那么对于BST有一个简单的优化,就是将原来的数列随机打乱后插入。那么其实Treap的思路也差不多,是在插入一个点的时候,然后给它一个随机的优先级,然后通过旋转使它保持堆性质。那么为什么旋转能在保持BST性质下,修改出Heap性质呢。先来看图。
这里写图片描述
很显然地,就是根节点在左旋的时候,根节点变成了右节点的左结点(有点绕),然后把原来右节点的左子树变成它的右子树,然后这样很显然是会保持BST性质的,右旋则反过来。
所以其实不必硬记,第一是打多了就熟悉了,第二是这是可以现场推导的,如果硬记反而麻烦←_←
那么有了旋转来看插入,只需要将一个点按照BST的插入方法插入为根节点,然后再往根节点往回递归的过程中,不停旋转,将不满足堆性质的子树变成满足的子树,最终会让整棵树依旧保持堆性质。
删除其实同理,只是将插入操作反过来运行,也就是将要删除的点通过旋转转成叶子结点,除了这个点以外使这棵BST依旧保持堆性质,然后直接删掉。
具体操作可以看代码。
最后写出来的代码应该是和set一样都具有一样功能的,但是Treap能实现的作用远远不止,还需要看后面的练习。

代码
#include <ctime>
#include <cstdio>
#include <cstdlib>

struct Treap {
    struct Node {
        Node *ch[2];
        int r;
        int v;
        int cmp(int x) const {
            if(x == v) return -1;
            return x < v ? 0 : 1;
        }
    } *root;

    void rotate(Node* &o, int d) {
        Node* k = o -> ch[d ^ 1];
        o -> ch[d ^ 1] = k -> ch[d];
        k -> ch[d] = o;
        o = k;
    }

    void insert(Node* &o, int x) {
        if(o == NULL) {
            o = new Node();
            o -> ch[0] = o -> ch[1] = NULL;
            o -> v = x;
            o -> r = rand();
        } else {
            int d = o -> cmp(x);
            if(o -> ch[d], x);
            if(o -> ch[d] -> r > o -> r) {
                rotate(o, d ^ 1);
            }
        }
    }

    void remove(Node* &o, int x) {
        int d = o -> cmp(x);
        if(d == -1) {
            if(o -> ch[0] == NULL) {
                o = o -> ch[1];
            } else {
                if(o -> ch[1] == NULL) {
                    o = o -> ch[0];
                } else {
                    int d2 = (o -> ch[0] -> r > o -> ch[1] -> r ? 1 : 0);
                    rotate(o, d2);
                    remove(o -> ch[d2], x);
                }
            }
        } else {
            remove(o -> ch[d], x);
        }
    }

    int find(Node* o, int x) {
        while(o != NULL) {
            int d = o -> cmp(x);
            if(d == -1) return 1;
             else o = o -> ch[d];
        }
        return 0;
    }
} treap;

int main(void) {
    srand(time(0));
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 0; i < n; ++i) {
        int x;
        scanf("%d", &x);
        treap.insert(treap.root, x);
    }

    for(int i = 0; i < m; ++i) {
        int a;
        scanf("%d", &a);
        int b;
        if(a == 0) {
            scanf("%d", &b);
            treap.remove(treap.root, b);
        } else {
            scanf("%d", &b);
            printf("%s\n", treap.find(treap.root, b) ? "existent" : "nonexistent");
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值