洛谷P3369题解:Treap动态数据结构详解与代码实现

一、题目解读
洛谷P3369题要求处理动态区间内的数据操作,涉及插入、删除及查询等。题目难点在于维护数据的动态平衡与高效查找,传统数据结构难以满足其性能需求。因此,需采用具有随机化平衡特性的Treap(树堆)来应对。
二、解题思路
采用Treap(树堆)结构,结合二叉搜索树的有序性与堆的优先级特性。通过随机生成节点优先级,在插入或删除时自动调整树形结构,避免极端不平衡情况。核心思路为:利用随机优先级驱动旋转操作,确保树高logN级别,从而维持整体操作O(logN)复杂度。
三、解题步骤
1. 构建Treap基础框架:定义节点结构(值、大小、计数、优先级、左右子树),初始化随机优先级避免固定规律。
2. 旋转操作优化平衡:通过左旋/右旋调整子树位置,依据优先级判断旋转条件(子节点优先级更高时触发)。
3. 动态维护:
○ 插入:递归至对应位置,更新节点计数与大小,必要时旋转;
○ 删除:若重复值则减计数,否则递归处理并合并子树,保证删除后仍平衡。
4. 更新机制:每次操作后自底向上更新节点size(子树+自身)。
四、代码与注释
#include <iostream>
#include <cstdlib>
#include <climits>
using namespace std;
const int INF = 1e8; // 处理大数值范围,避免优先级重复
struct Node {
int val, size, cnt; // 值、子树大小、重复计数
int priority; // 随机优先级
Node *l, *r; // 左右子树
Node(int v) : val(v), size(1), cnt(1), l(nullptr), r(nullptr) {
priority = rand() % INF; // 初始化随机优先级
}
};
class Treap {
private:
Node *root;
void update(Node *node) { // 更新节点信息
if(!node) return;
node->size = node->cnt; // 初始大小为自身
if(node->l) node->size += node->l->size; // 累加左子树
if(node->r) node->size += node->r->size; // 累加右子树
}
void rotateLeft(Node *&node) { // 左旋(右子树优先级高时)
Node *temp = node->r;
node->r = temp->l; // 调整子树连接
temp->l = node;
update(node); // 更新受影响节点信息
update(temp);
node = temp;
}
void rotateRight(Node *&node) { // 右旋(左子树优先级高时)
// 对称处理,逻辑同左旋
}
void insert(Node *&node, int val) {
if(!node) { // 空节点直接创建
node = new Node(val);
return;
}
if(val == node->val) { // 重复值,计数+1
node->cnt++;
} else if(val < node->val) {
insert(node->l, val); // 递归左子树
if(node->l->priority > node->priority) // 左子树优先级高,右旋
rotateRight(node);
} else {
// 递归右子树,优先级高则左旋
}
update(node); // 操作后更新节点
}
void remove(Node *&node, int val) {
if(!node) return;
if(val < node->val) {
remove(node->l, val);
} else if(val > node->val) {
remove(node->r, val);
} else {
if(node->cnt > 1) { // 重复值,直接减计数
node->cnt--;
} else { // 删除节点
if(!node->l ||!node->r) { // 无子树或仅单侧子树
Node *temp = node->l? node->l : node->r; // 合并子树
delete node; // 释放原节点
node = temp; // 更新根指针
} else { // 双侧子树,递归删除
if(node->l->priority > node->r->priority) { // 左子树优先级高,右旋后递归删右子树
rotateRight(node);
remove(node->r, val);
} else {
// 对称处理,左旋后递归删左子树
rotateLeft(node);
remove(node->l, val);
}
}
}
}
if(node) update(node);
}
int getRank(Node *node, int val) {
if(!node) return 0;
if(val < node->val) return getRank(node->l, val);
int leftSize = node->l ? node->l->size : 0;
if(val == node->val) return leftSize + 1;
return leftSize + node->cnt + getRank(node->r, val);
}
int getValue(Node *node, int rank) {
if(!node) return INF;
int leftSize = node->l ? node->l->size : 0;
if(rank <= leftSize) return getValue(node->l, rank);
if(rank <= leftSize + node->cnt) return node->val;
return getValue(node->r, rank - leftSize - node->cnt);
}
int getPre(Node *node, int val) {
if(!node) return -INF;
if(node->val >= val) return getPre(node->l, val);
return max(node->val, getPre(node->r, val));
}
int getNext(Node *node, int val) {
if(!node) return INF;
if(node->val <= val) return getNext(node->r, val);
return min(node->val, getNext(node->l, val));
}
public:
Treap() : root(nullptr) { srand(time(0)); }
void insert(int val) { insert(root, val); }
void remove(int val) { remove(root, val); }
int getRank(int val) { return getRank(root, val); }
int getValue(int rank) { return getValue(root, rank); }
int getPre(int val) { return getPre(root, val); }
int getNext(int val) { return getNext(root, val); }
};
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
Treap treap;
int n, opt, x;
cin >> n;
while(n--) {
cin >> opt >> x;
switch(opt) {
case 1: treap.insert(x); break;
case 2: treap.remove(x); break;
case 3: cout << treap.getRank(x) << '\n'; break;
case 4: cout << treap.getValue(x) << '\n'; break;
case 5: cout << treap.getPre(x) << '\n'; break;
case 6: cout << treap.getNext(x) << '\n'; break;
}
}
return 0;
}五、总结
Treap通过随机化优先级与旋转机制,巧妙结合了BST的查询效率与堆的平衡性,适用于动态区间操作的场景。其时间复杂度稳定在O(logN),且无需复杂的手动平衡调整,是解决此类问题的经典方案。理解其旋转逻辑与递归设计,对提升动态数据结构实践能力具有重要意义。
原创内容 转载请注明出处





