非旋转Treap

Treap是一种平衡二叉树,同时也是一个堆。它既具有二叉查找树的性质,也具有堆的性质。在对数据的查找、插入、删除、求第k大等操作上具有期望O(log2n)的复杂度。 
    Treap可以通过节点的旋转来实现其维持平衡的操作,详见旋转式Treap. 而旋转式Treap在对区间数据的操作上无能为力,这就需要非旋转式Treap来解决这些区间问题。

非旋转式Treap支持的操作

基本操作:

操作说明实现复杂度
Build 构造Treap O(n)
Merge 合并Treap O(log2n)
Split 拆分Treap O(log2n)
NewNode 新建节点 O(1)

可支持操作:

操作说明(实现)实现复杂度
Insert NewNode + Merge O(log2n)
Delete Split + Split + Merge O(log2n)
FindKth Split + Split O(log2n)
Query Split + Split O(log2n)
Cover Split + Split + Merge O(log2n)

基本操作

1. Build

    类似于笛卡尔树的构造(详细见笛卡尔树),非旋转式Treap也可以通过与栈的集合来达到平摊O(n)的复杂度。具体为:

(1) 通过快排等排序手段对原始数据序列进行排序,这主要是为了使Treap满足二叉查找特性 
(2) 当前Treap中的从根开始的右子节点、右子节点的右子节点...链上的所有节点push到一个栈中,栈底为根节点 
(3) 对排序后的序列,从头开始,执行: 
(3.1) 生成一个新的Treap节点,节点中会有随机生成的priority值用来实现堆的结构 
(3.2) 从栈顶向栈底查找,同时栈顶元素出栈,直到栈顶元素的priority小于当前节点的priority,记录下当前栈顶节点为P 
(3.3) 将P之前出栈的那个节点置为待插入节点的左子结点,同时将待插入节点置为P的右子节点,再将带插入节点入栈

2. Merge

    类似于左倾堆的Merge操作,可以在O(log2n)的时间复杂度内完成Merge操作。具体为:

(1) 如果一个Treap为空,则返回另外的Treap 
(2) 如果选择两个Treap中堆顶元素的priority值最小为的堆,“较小堆”的堆顶成为新堆的堆顶,然后递归调用 Merge,对较小堆的堆顶元素的右子节点和较大堆进行合并操作,并将返回的结果置为“较小堆”堆顶元素的右子节点 
(3) 对新堆的顶点进行维护,即维护堆顶节点的size等信息

3. Split

    对于一个Treap,按照他的第k位进行拆分,则可以按照类似快排算法寻找第k大元素的步骤,返回结果为一个pair,即最大元素为全局第k大的子树根节点和最小元素为全局第k+1大的子树根节点构成的pair。具体为:

(1) 若当前节点x的左子树中的size大于等于k,则进入左子树进行拆分,返回结果y(为一个pair)。 
(1.1) 然后将第k+1大及其之后的节点构成的子树挂在到x的左子树上。即将x的左子结点置为y的second,然后更新x节点的size等信息。 
(1.2) 然后将y的second指向x节点,即最小元素为全局第k+1大的子树的根节点 
(2) 若当前节点x的左子树的size小于k,则进入右子树进行拆分,返回结果y 
(2.1) 然后将第k大及其之前的节点构成的子树挂在到x的右子树上。即将x的右子节点置为y的first,然后更新x节点的size信息。 
(2.2) 然后将y的first指向x节点,即最大元素为全局第k大的子树的根节点 
(3) 返回y

实现(c++)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include<iostream>
#include<algorithm>
using namespace std;
#define MAX_NUM 100
struct TreapNode{
    int key;
    int priority;
    int size;
    TreapNode* left;
    TreapNode* right;
    TreapNode(int k){
        key = k;
        size = 1;
        priority = rand();
        left = right = NULL;
    }
    void Update(){
        size = 1;
        if (left)
            size += left->size;
        if (right)
            size += right->size;
    }
};
 
int GetSize(TreapNode* node){
    if (node)
        return node->size;
    return 0;
}
void Build(int *arr, int n){
    sort(arr, arr + n); //先进行排序
    TreapNode* stack[MAX_NUM];
    TreapNode* last;
    int p = 0;
    TreapNode* new_node;
    for (int i = 0; i < n; i++){
        last = NULL;
        new_node = new TreapNode(arr[i]);
        //直到栈为空,或者栈顶元素priority小于当前节点的priority
        while (p && stack[p]->priority > new_node->priority){
            last = stack[p--];
        }
        stack[p]->right = new_node;  //新节点作为p的右子节点
        new_node->left = last;       //前一次出栈的节点作为 新节点的左子结点
        stack[++p] = new_node; //新插入元素入栈
    }
}
 
TreapNode* Merge(TreapNode* treap1, TreapNode* treap2){
    if (!treap1)
        return treap2;
    if (!treap2)
        return treap1;
 
    if (treap1->priority < treap2->priority){
        treap1->right = Merge(treap1->right, treap2);
        treap1->Update();
        return treap1;
    }
    else{
        treap2->left = Merge(treap1, treap2->left);
        treap2->Update();
        return treap2;
    }
}
 
typedef pair<TreapNode*, TreapNode*> TreapPair;
TreapPair Split(TreapNode* treap, int k){
    if (!treap){
        return TreapPair(NULL, NULL);
    }
    TreapPair result;
    if (treap->left->size >= k){
        result = Split(treap->left, k);
        treap->left = result.second;
        treap->Update();
        result.second = treap;
    }
    else{
        result = Split(treap->right, k - treap->left->size - 1);
        treap->right = result.first;
        treap->Update();
        result.first = treap;
    }
    return result;
}
 
//注意为treapNode指针引用
int FindKth(TreapNode*& treap, int k){
    TreapPair x = Split(treap, k - 1);
    TreapPair y = Split(x.second, 1);
    int result = y.first->key;
    treap = Merge(x.first, y.first);
    treap = Merge(treap, y.second);
}
 
//询问一个数是第几大
int GetKth(TreapNode* treap, int v){
    if (!treap)
        return 0;
    if (v < treap->key)
        return GetKth(treap->left, v);
    else
        return GetKth(treap->right, v) + GetSize(treap->left) + 1;
}
 
void Insert(TreapNode*& treap, int v){
    int k = GetKth(treap, v);
    TreapPair result = Split(treap, k);
    TreapNode* new_treap = new TreapNode(v);
    treap = Merge(result.first, new_treap);
    treap = Merge(treap, result.second);
}
 
void Delete(TreapNode*& treap, int k){
    TreapPair x = Split(treap, k - 1);
    TreapPair y = Split(x.second, 1);
    treap = Merge(x.first, y.second);
}

 

参考

非旋转Treap

posted @   农民伯伯-Coding  阅读(681)  评论(0)    收藏  举报
编辑推荐:
· 记一次 .NET某旅行社酒店管理系统 卡死分析
· 长文讲解 MCP 和案例实战
· Hangfire Redis 实现秒级定时任务,使用 CQRS 实现动态执行代码
· Android编译时动态插入代码原理与实践
· 解锁.NET 9性能优化黑科技:从内存管理到Web性能的最全指南
阅读排行:
· 一天 Star 破万的开源项目「GitHub 热点速览」
· 瞧瞧别人家的日期处理,那叫一个优雅!
· 使用TypeScript开发微信小程序(云开发)-入门篇
· 没几个人需要了解的JDK知识,我却花了3天时间研究
· 定时任务稳定性解决方案-healthchecks监控系统
点击右上角即可分享
微信分享提示