非旋转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); } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次 .NET某旅行社酒店管理系统 卡死分析
· 长文讲解 MCP 和案例实战
· Hangfire Redis 实现秒级定时任务,使用 CQRS 实现动态执行代码
· Android编译时动态插入代码原理与实践
· 解锁.NET 9性能优化黑科技:从内存管理到Web性能的最全指南
· 一天 Star 破万的开源项目「GitHub 热点速览」
· 瞧瞧别人家的日期处理,那叫一个优雅!
· 使用TypeScript开发微信小程序(云开发)-入门篇
· 没几个人需要了解的JDK知识,我却花了3天时间研究
· 定时任务稳定性解决方案-healthchecks监控系统