谈谈数据结构-Treap
分类:Data Structure
Treap
1. Treap原理
Treap
=(Binary Search)Tree
+ Heap
。
Treap 去掉fix域和rotate过程,就是一个很简单的排序二叉树BST。
虽然在插入数据完全离散的情况下,BST的期望复杂度也是
O(logn)
O
(
log
n
)
,但是,无论是在算法竞赛中,还是实际情况中,很难保证输入数据是完全离散的。所以在很多情况,BST是很容易退化成链式。
而fix,rotate的功能就是通过随机得到的fix来确定rotate是左旋还是右旋。说白了就是按照BST去插入节点,按照Heap的性质去调整树的高度、去维护二叉树的稳定性。
在随机数生成器足够稳定的情况下,具有非常良好的性能。Treap基本操作的期望复杂度是
O(logn)
O
(
log
n
)
。
Treap相对AVL、SBT、Splay等平衡树来说,编程复杂度小得多!而且,Splay是一个均摊的复杂度
O(logn)
O
(
log
n
)
,在某一些数据上,并不如Treap稳定~~
总之,Treap简单!实用!
Treap本身并不复杂。但是,这种通过增加随机域,保证数据离散性的做法很棒。
还不明白的,可以参考以下链接:
- ByVoid 的《随机平衡二叉查找树 Treap 的分析与应用》
- LoliconAutomaton的《Treap以及名次树》
- 御坂12207號的《【算法笔记】Treap平衡树》
2. 模板代码
以下代码是旋转版本的Treap的,功能比较简单。。
支持内存池静态分配节点内存+内存回收。。
Treap 的功能还有很多诶。。
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int inf = 0x3f3f3f3f;
const long long infl = 0x3f3f3f3f3f3f3f3fLL;
template<typename T> inline void umax(T &a, T b) { a = max(a, b); }
template<typename T> inline void umin(T &a, T b) { a = min(a, b); }
void debug() { cout << endl; }
template<typename T, typename ...R>
void debug (T f, R ...r) { cout << "[" << f << "]"; debug (r...); }
template<class T> struct Treap {
static const int TREAP_SIZE = 1000005;
struct TNode {
int fix;
T val;
int size;
int cnt;
TNode* ch[2];
void init() {
fix = rand();
size = cnt = 0;
ch[0] = ch[1] = NULL;
}
TNode() { init(); }
TNode(T v) : val(v) { init(); }
} node[TREAP_SIZE], *pRoot;
queue<TNode*> trash; // 内存回收
int tot;
void init() {
tot = 0;
pRoot = NULL;
while (!trash.empty()) trash.pop();
}
Treap() { init(); }
inline int size(TNode* rt) { return rt == NULL ? 0 : rt->size; }
void pushUp(TNode* rt) {
rt->size = rt->cnt;
rt->size += size(rt->ch[0]);
rt->size += size(rt->ch[1]);
}
TNode* cre_node(T val) {
TNode* pt = NULL;
if (!trash.empty()) {
pt = trash.front(); trash.pop();
} else {
pt = &node[tot++];
}
pt->init();
pt->val = val;
pt->size = pt->cnt = 1;
return pt;
}
/**
* 对子树进行旋转
* @param rt 旋转前子树根节点
* @param d 左旋false,右旋true
* @return 旋转后子树根节点
*/
TNode* rotate(TNode* rt, bool d) {
if (rt == NULL || rt->ch[d ^ 1] == NULL) return rt;
TNode* pt = rt->ch[d ^ 1];
rt->ch[d ^ 1] = pt->ch[d];
pt->ch[d] = rt;
return pt;
}
/**
* 在子树中插入一个节点
* @param rt 插入前子树根节点指针
* @param val 插入的值
* @return 插入后子树根节点指针
*/
TNode* insert(TNode* rt, const T& val) {
if (rt == NULL) return cre_node(val);
if (rt->val == val) {
rt->cnt += 1;
rt->size += 1;
return rt;
}
bool d = (val > rt->val);
rt->ch[d] = insert(rt->ch[d], val);
if (rt->ch[d]->fix < rt->fix) rt = rotate(rt, d ^ 1);
pushUp(rt);
return rt;
}
/**
* 在子树中删除一个节点,分两种情况旋转
* @param rt 删除前子树根节点指针
* @param val 删除的值
* @return 删除后子树根节点指针
*/
TNode* remove(TNode* rt, const T& val) {
if (rt == NULL) return NULL;
if (rt->val == val) {
if (rt->cnt > 1) {
rt->cnt -= 1;
return rt;
}
if (rt->ch[0] == NULL || rt->ch[1] == NULL) {
// 当前节点只有一个子节点,直接删除
trash.push(rt);
if (rt->ch[0] != NULL) return rt->ch[0];
else return rt->ch[1];
} else {
// 当前节点只有两个子节点,先旋转再删除
bool d = (rt->ch[0]->fix < rt->ch[1]->fix);
rt = rotate(rt, d);
rt->ch[d] = remove(rt->ch[d], val);
}
} else {
bool d = (val > rt->val);
rt->ch[d] = remove(rt->ch[d], val);
d = (val > rt->val);
if (rt->ch[d] && rt->ch[d]->fix < rt->fix) rt = rotate(rt, d ^ 1);
}
pushUp(rt);
return rt;
}
/**
* 获取子树的第k小数, indexed from 0
* @param rt 子树根节点
* @return k
*/
T kth(TNode* rt, const int& k) {
assert(k < size(rt));
int lsz = size(rt->ch[0]);
if (k < lsz) return kth(rt->ch[0], k);
else if (k < lsz + rt->cnt) return rt->val;
else return kth(rt->ch[1], k - lsz - rt->cnt);
}
/**
* 获取在子树中,按从小到大,小于val的数有多少个
* @param rt 子树根节点
* @param val 值
* @return 个数(排名)
*/
int rank(TNode* rt, const T& val) {
if (rt == NULL) return 0;
if (val == rt->val) return size(rt->ch[0]);
else if (val < rt->val) return rank(rt->ch[0], val);
else return rank(rt->ch[1], val) + size(rt->ch[0]) + rt->cnt;
}
/**
* 顺序输出子树序列
* @param rt 子树根节点
*/
void print(TNode* rt) {
if (rt == NULL) return;
print(rt->ch[0]);
debug(rt->val, rt->cnt);
print(rt->ch[1]);
}
};
Treap<int> treap;
int main() {
#ifdef ___LOCAL_WONZY___
freopen("input.txt", "r", stdin);
#endif // ___LOCAL_WONZY___
// int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i = 1; i <= 10; ++i) {
treap.pRoot = treap.insert(treap.pRoot, i);
}
treap.pRoot = treap.insert(treap.pRoot, 5);
treap.pRoot = treap.insert(treap.pRoot, 5);
treap.pRoot = treap.remove(treap.pRoot, 5);
treap.print(treap.pRoot);
debug("kth:", treap.kth(treap.pRoot, 7));
debug("rank", treap.rank(treap.pRoot, 7));
#ifdef ___LOCAL_WONZY___
cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << "ms." << endl;
#endif // ___LOCAL_WONZY___
return 0;
}