treap(tree+heap就是二叉搜索树+堆)

Treap是一种结合了二叉搜索树和堆性质的数据结构,通过随机生成的修正值维持树的平衡,提供高效的插入、删除和查找操作。本文深入解析Treap的定义、特点及其在信息学竞赛中的广泛应用。

1、Treap的定义
Treap是一棵二叉搜索树,只是每个节点多了一个优先级fix,对于每个节点,该节点的优先级小于等于其所有孩子的优先级。当然,引入优先级fix的目的就是防止退化成一条链,从而影响查找效率。
所以,这样看来就是:Treap中对于节点的关键字key来说,它是一棵二叉搜索树,而对于fix来说,它是一个最小堆,所以Treap可以看成是Tree+Heap,只是这里的Heap不一定是完全二叉树。
Treap的平均时间复杂度为log(n).
如下图: 在这里插入图片描述
修正值是节点在插入到TreapTreap中时随机生成的一个值,它与节点的值无关。下述代码给出了TreapTreap的一般定义。
struct Treap_Node {
Treap_Node *left, *right; //节点的左右子树的指针
int value, fix; //节点的值和修正值
};

基本操作:插入x数、删除x数、查询x数的排名、查询排名为x的数、求x的前驱、求x的后继。
treap同时满足两个性质的方法,首先令其满足二叉排序树性质;通过左旋或者右旋,令其同时满足堆的性质。
左旋操作
在这里插入图片描述
右旋操作
在这里插入图片描述
左旋、右旋代码

void Treap_Left_Rotate(Treap_Node *&a) {//左旋 节点指针一定要传递引用
    Treap_Node *b = a->right;
    a->right = b->left;
    b->left = a;
    a = b;
}
void Treap_Right_Rotate(Treap_Node *&a) {//右旋 节点指针一定要传递引用
    Treap_Node *b = a->left;
    a->left = b->right;
    b->right = a;
    a = b;
}

初学者对左旋、右旋总记不住,其实很简单,就以右旋举例,左儿子B转到父A的位置,父A当然就成了B的右子树了,A以前是B的父节点,A比B大,旋转后为了保持BST结构,肯定A是B的右子树了,如果B以前左右子树都有,再加上A成为右子树,这样B就有三个子树了,破坏了BST结构,只能把B的子树给A一个,当然只能给B以前的右子树了。
Treap的插入操作
给节点随机分配一个优先级,先和二叉排序树(又叫二叉搜索树)的插入一样,按key插入到相应地方,然后再按优先级fix维护堆的性质。
插入写成递归形式的话,只需要在递归调用完成后判断是否满足堆性质,如果不满足就旋转,实现相对简单。其插入过程示例图如下:
在已知的Treap插入值为4的元素
找到插入的位置后,随机生成的修正值为15
在这里插入图片描述
新建的节点与他的父节点3不满足堆序3为根的子树左旋,如图9
在这里插入图片描述
节点4与其父节点5仍不满足最小堆序,对以节点55为根的子树右旋,如图10
在这里插入图片描述
至此,节点4与其父亲2满足堆序,调整结束。

时间复杂度:
由于旋转是O(1)的,最多进行h次(h是树的高度),插入的复杂度是O(h)的,在期望情况下h=O(log n),所以它的期望复杂度是O(log n)。

Treap_Node *root;
void Treap_Insert(Treap_Node *&P, int value) {//节点指针一定要传递引用
    if (!P) {//找到位置,建立节点
        P = new Treap_Node;
        P->value = value;
        P->fix=rand();//生成随机的修正值
    }
    else if (value <= P->value) {
        Treap_Insert(P->left, value);
        if (P->left->fix < P->fix)
        Treap_Right_Rotate(P);//左子节点修正值小于当前节点修正值,右旋当前节点
    }
    else {
        Treap_Insert(P->right, value);
        if (P->right->fix < P->fix)
        Treap_Left_Rotate(P);//右子节点修正值小于当前节点修正值,左旋当前节点
    }
}
int main() {
    Treap_Insert(root, 7);//在 Treap 中插入值为 7 的元素
    return 0;
}

Treap的删除操作
与BST一样,在Treap中删除元素要考虑多种情况
我们可以按照在BST中删除元素同样的方法来删除Treap中的元素
即用它的后继(或前驱)节点的值代替它,然后删除它的后继(或前驱)节点。
为了不使Treap向一边偏沉,我们需要随机地选取是用后继还是前驱代替它,并保证两种选择的概率均等。
上述方法期望时间复杂度为O(logN)。
但是上述方法并没有充分利用Treap随机性质,每次删除一个节点,就破坏了fix堆的结构,我们左旋或右旋操作后,就不一定用后继(或前驱)替换删除的节点。

我们给出一种更为通用的删除方法,这种方法是基于旋转调整的
首先要在Treap树中找到待删除节点的位置,然后分情况讨论:
情况一,该节点为叶节点或链节点,则该节点是可以直接删除的节点,若该节点有非空子节点,用非空子节点代替该节点的,否则用空节点代替该节点,然后删除该节点。
情况二,该节点有两个非空子节点
我们的策略是通过旋转,使该节点变为可以直接删除的节点。如果该节点的左子节点的修正值小于右子节点的修正值,右旋该节点,使该节点降为右子树的根节点,然后访问右子树的根节点,继续讨论;反之,左旋该节点,使该节点降为左子树根节点,然后访问左子树的根节点,继续讨论,知道变成可以直接删除的节点。

下面给一个删除例子:在Treap中删除是6的元素
如图11
先在Treap中找到6的位置
在这里插入图片描述
发现节点6有两个子节点,且左子节点的修正值小于右子节点的修正值,需要右旋节点6,如图12。(记住往大的那边转,右给右)
在这里插入图片描述

旋转后,节点6仍有两个节点,右子节点修正值较小,于是左旋节点6,如图 13。(往大的那边转)
在这里插入图片描述
此时,节点6只有一个子节点,可以直接删除,用它的左子节点代替它,删除本身,如图14。我们发现这次是用他的前驱代替
在这里插入图片描述
我们发现在删除的过程中根本就不需要用到删除节点的父节点,只是删除节点和左右子树打交道,不像BST的删除用到父节点。

删除的复杂度稍高,但是期望时间仍为O(logN)
但是在程序中更容易实现
下述代码给出了后一种(即上述图例中)的删除方法
在给定的Treap中删除值为6的节点。

BST_Node *root;
void Treap_Delete(Treap_Node *&P, int value) {//节点指针要传递引用
    if (value == P->value) {//找到要删除的节点 对其删除
        if (!P->right or !P->left) {//情况一,该节点可以直接被删除
            Treap_Node *t = P;
            if (!P->right) P = P->left; //用左子节点代替它
            else P = P->right; //用右子节点代替它
            delete t; //删除该节点
        }
        else {//情况二
            if (P->left->fix < P->right->fix) {//左子节点修正值较小,右旋
                Treap_Right_Rotate(P);
                Treap_Delete(P->right, value);
            }
            else {//左子节点修正值较小,左旋
                Treap_Left_Rotate(P);
                Treap_Delete(P->left, value);
            }
        }
    }
    else if (value < P->value) Treap_Delete(P->left, value); //在左子树查找要删除的节点
    else Treap_Delete(P->right, value); //在右子树查找要删除的节点
}
int main() {
    Treap_Delete(root, 6);//在Treap中删除值为6的元素
    return 0;
}

Treap的特点

1、
Treap简明易懂

Treap只有两种调整方式,左旋和右旋
,而且即使没有严密的数学证明和分析,Treap的构造方法、平衡原理也是不难理解的,
只要能够理解BST和堆的思想,理解Treap就不在话下

2、Treap易于编写,Treap只需维护一个满足堆序的修正值,修正值一经生成无需修改
,相比较其他各种平衡树,Treap拥有最少的调整方式,仅仅两种相互对称的旋转
所以Treap当之无愧是最易于编码调试的一种平衡树。
3、Treap稳定性佳

Treap的平衡性虽不如AVL、红黑树、SBT等平衡树
,但是Treap也不会退化,可以保证期望O(logN)的深度,Treap的稳定性取决于随机数发生器

4、Treap具有严密的数学证明

Treap期望O(logN的深度,是有严密的数学证明的
但这不是本文介绍的重点,略去。
5、Treap具有良好的实践效果
各种实际应用中,Treap的稳定性表现得相当出色,没有因为任何的构造出的数据而退化,
于是在信息学竞赛中,不少选手习惯于使用Treap,均取得了不俗的表现。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值