数据结构-树堆(Treap)

现在给你一个问题,每次右两种操作,第一种操作是向集合中添加一个数x,第二种操作是询问集合中不大于k的最大的数是多少。现在我们不用任何stl里面的容器的情况下,你要怎样解决这个问题呢?一个最暴力的方法是,从一个数组存数,然后每次询问去遍历这个数组,这样每次插入数的时间复杂度是O(1),而查询时间复杂是O(n),所以这种方法对于查询操作比较多时,会很慢.有基础的同学可能会想到用二分去找,但是用二分必须要求整个序列是有序的,所以要保证你的数组是有序的,每次需要把一个数插入到合适的位置,然后把这个数后面的都往后移动一位,这样的话询问时间复杂度是O(logn),但是插入时间复杂度是O(n),这种方法对于插入操作比较多时,也很慢。跟上面暴力的方法相比复杂度没有本质的变化。当然这个问题好像可以用跳表解决,也就是在链表上做二分,当然这个我不会。也不是今天要谈论的内容。学过二叉排序树的同学,也许会想到用二叉排序树来做,这样每次插入的复杂度是树的高度,询问的复杂度也是树的高度。这种方法比上面的两种方法都要高效,但是有一个致命的弱点,就是当所给的序列是一个递增序列时,你的二叉树会退化成一条链,这样每次插入的复杂度和询问的复杂度都是O(n).那么我们有没有一种方法可以避免这种情况呢?答案是有的。就是今天我们要讲的数据结构Treap,树堆,是Tree + Heap的结合体。同时满足二叉排序树和堆的性质。在树堆中每个节点除了值value外另外还加入一个值w,这个值每次初始化时时随机的一个值。value满足二叉排序树的特点,而w满足堆的特点。这样每次插入一个树时,我们按value的值插入,也就是插入之后情况一定时满足二叉排序树的,但是因为我们的w值是随机生成的,所以有可能会不满足堆的性质,不满足怎么办?当然是调整啊,在堆中我们调整的方法是跟父亲节点的值交换,在这里面当然不能也这样做,因为这样会破坏二叉排序树的性质。这里我们调整只能通过旋转根来完成这个工作,这样既不会破坏二叉排序树的性质,也可以调整w,使得其满足堆的性质。旋转有左旋和右旋,左旋是把根的右儿子节点旋转到根,根变成右儿子节点的左孩子,原来右儿子的左儿子节点(如果有的话,也有可能没有)变成原来根节点的右儿子。右旋和左旋类似。这样就完成了调整这个操作。你肯定要问,这样做有什么用?这样做的目的,是通过w的随机性,保证树的平衡性,通俗来说就是是树不要退化成链。你可以看到,Treap这个东西依赖与随机来保证树的平衡,这个东西也是看脸的,但是平均,也就是期望的复杂度是O(logn).下面是一道实际的题目,这个题目的数据比较弱,不用Treap,只用二叉排序树也能过。

#1325 : 平衡树·Treap

时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB

描述

小Ho:小Hi,我发现我们以前讲过的两个数据结构特别相似。

小Hi:你说的是哪两个啊?

小Ho:就是二叉排序树和堆啊,你看这两种数据结构都是构造了一个二叉树,一个节点有一个父亲和两个儿子。 如果用1..n的数组来存储的话,对于二叉树上的一个编号为k的节点,其父亲节点刚好是k/2。并且它的两个儿子节点分别为k*2和k*2+1,计算起来非常方便呢。

小Hi:没错,但是小Hi你知道有一种办法可以把堆和二叉搜索树合并起来,成为一个新的数据结构么?

小Ho:这我倒没想过。不过二叉搜索树满足左子树<根节点<右子树,而堆是满足根节点小于等于(或大于等于)左右儿子。这两种性质是冲突的啊?

小Hi:恩,你说的没错,这两种性质的确是冲突的。

小Ho:那你说的合并是怎么做到的?

小Hi:当然有办法了,其实它是这样的....

提示:Tree+Heap?

输入

第1行:1个正整数n,表示操作数量,10≤n≤100,000

第2..n+1行:每行1个字母c和1个整数k:

若c为'I',表示插入一个数字k到树中,-1,000,000,000≤k≤1,000,000,000

若c为'Q',表示询问树中不超过k的最大数字

输出

若干行:每行1个整数,表示针对询问的回答,保证一定有合法的解

样例输入
5
I 3
I 2
Q 3
I 5
Q 4
样例输出
3
3

#include 
using namespace std;
const int inf = 0x3f3f3f3f;
struct node{
    node *L, *R;//左右儿子节点
    node *F;//父亲节点
    node(){
        L = NULL;
        R = NULL;
        F = NULL;
        w = rand() % inf;
    }
    node(int _value){
        value = _value;
        w = rand() % inf;
        L = NULL;
        R = NULL;
        F = NULL;
    }
    int value;
    int w;//随机值
};
int n;

class Treap{
public:
    node *Root;
    Treap()
    {
        Root = NULL;
    }
    node* insert(node *root, int value)//插入一个值,返回该节点
    {
        if(root == NULL)
        {
            Root = new node(value);
            return Root;
        }
        else
        {
            if(value <= root->value)
            {
                if(root->L == NULL)
                {
                    root->L = new node(value);
                    root->L->F = root;
                    return root->L;
                }
                else
                {
                    return insert(root->L, value);
                }
            }
            else
            {
                if(root->R == NULL)
                {
                    root->R = new node(value);
                    root->R->F = root;
                    return root->R;
                }
                else
                {
                    return insert(root->R, value);
                }
            }
        }
    }
    void adjust(node *p)//调整树的结构,使其满足大根堆的性质
    {
        if(p == NULL) return;
        if((p->L == NULL && p->R != NULL && p->w < p->R->w) || (p->L != NULL && p->R != NULL && p->w < p->R->w && p->L->w <= p->R->w))//左旋
        {
            if(p->F != NULL)
            {
                if(p->F->L == p)
                {
                    p->F->L = p->R;
                }
                else p->F->R = p->R;
            }
            if(p == Root) Root = p->R;
            node *rson = p->R;
            rson->F = p->F;
            p->F = rson;
            node *temp = rson->L;
            rson->L = p;
            p->R = temp;
            if(temp != NULL) temp->F = p;
            adjust(rson);
        }
        else if((p->L != NULL && p->R == NULL && p->w < p->L->w) || (p->L != NULL && p->R != NULL && p->w < p->L->w && p->R->w <= p->L->w))//右旋
        {
            if(p->F != NULL)
            {
                if(p->F->L == p)
                {
                    p->F->L = p->L;
                }
                else p->F->R = p->L;
            }
            if(p == Root) Root = p->L;
            node *lson = p->L;
            lson->F = p->F;
            p->F = lson;
            node *temp = lson->R;
            lson->R = p;
            p->L = temp;
            if(temp != NULL) temp->F = p;
            adjust(lson);
        }
        else return;

    }
    int query(int key, node *root)//询问不大于key的最大值
    {
        if(root == NULL) return -inf;
        if(key == root->value) return key;
        if(key < root->value) return query(key, root->L);
        if(key > root->value) return max(root->value, query(key, root->R));

    }
};
int main()
{
    scanf("%d", &n);
    Treap t;
    char op[10];
    int key;
    for(int i = 1; i <= n; i++)
    {
        scanf("%s %d", op, &key);
        if(op[0] == 'I')
        {
            node *newnode = t.insert(t.Root, key);
            t.adjust(newnode->F);
        }
        else
        {
            printf("%d\n", t.query(key, t.Root));
        }
    }
    return 0;
}

你可能感兴趣的:(平衡树)