测试题目来自 http://codevs.cn/problem/1164/
部分内容来自网络
想要了解treap树,你先要知道什么是二叉搜索树。
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
二叉搜索树算法实现很简单,请各位自行百度。但是,二叉搜索树往往会暴露一些问题。例如当读入数据是高度有序的,如1 2 3 4 5, 树就会变成一条链:
1
2
3
4
5
这样的话,树就变成了一条链,查找和插入效率仅仅相当于链表,这自然是我们不愿意看到的。所以,前人想了一大堆办法使得搜索树尽量扁平,达到O(logn)的期望复杂度。其中有avl rb_tree sbt splay等许多优化方式。而其中最简单易行的就是treap。
所谓treap,顾名思义就是tree+heap,使得二叉排序树既满足树的性质又满足堆的性质(这里堆不一定是完全二叉树)。树的性质依靠存的数据完成,堆的性质则靠一个随机数据prior存储。我们在插入节点时向新节点分配一个优先值。如果新节点优先级小于其父节点,则违背了小根堆的性质,我们依靠旋转来维护堆性质。
旋转有两种方式
①: 左左情况旋转
从图中可以看出,当我们插入“节点12”的时候,此时“堆性质”遭到破坏,必须进行旋转,我们发现优先级是6<9,所以就要进行
左左情况旋转,最终也就形成了我们需要的结果。
②: 右右情况旋转
既然理解了”左左情况旋转“,右右情况也是同样的道理,优先级中发现“6<9”,进行”右右旋转“最终达到我们要的效果。
void change_right(node<T>* &n)
{
/*
* 向右旋转(顺时针) 把当前节点的左孩子作为父节点.
*/
node<T> *temp = n->left_child;
n->left_child = temp->right_child;
temp->right_child = n;
n = temp;
temp = 0;
}
void change_left (node<T>* &n)
{
/*
* 向左旋转 与向右相反
*/
node<T> *temp = n->right_child;
n->right_child = temp->left_child;
temp->left_child = n;
n = temp;
temp = 0;
}
理解了这两种旋转,我们只要对bst的put加以改造就可以维护treap性质:
void put (node<T>* &n, T data)
{
/*节点为空,新建并插入数据*/
if (!n) {
n = new node<T>;
n->data = data;
n->left_child = n->right_child = 0;
n->prior = rand()%1000000007;
/*随机数据作为优先值*/
return;
}
/*否则左右查找*/
if (data == n->data) {
n->data = data;
//cout << data.first << " " << data.second << endl;
} else if (data < n->data) {
put (n->left_child, data);
if (n->left_child->prior < n->prior)
change_right(n);
/*如果左节点优先值比当前小, 即破坏了小根堆的性质
* 则向右旋转当前节点以维护堆性质
*/
} else {
put (n->right_child, data);
if (n->right_child->prior < n->prior)
change_left(n);
/*与上面刚好相反*/
}
}
如果你不奢求支持erase,treap就是这样简单了。但为了透彻了解treap,我们支持一下erase操作。删除的方法有两种。这里我们介绍treap独有的——将”结点下旋“,直到该节点成为叶子节点,删除之。首先找到删除的节点。为了支持小根堆性质,我们每次选择优先值较小的节点并向反方向旋转,将当前节点下旋直到没有孩子(叶子节点)。具体代码如下:
/*删除某个元素*/
void erase_T(node<T>* &tree, T n)
{
if (!tree)
return;
/*如果不是则按照bst左右查找*/
if (!(tree->data == n)) {
if (n < tree->data)
erase_T(tree->left_child, n);
else
erase_T(tree->right_child, n);
return;
}
/*如果已经没有左右节点, 删除之*/
if (!tree->left_child && !tree->right_child) {
delete tree;
tree = 0;
} else if (tree->left_child && tree->right_child){
/*如果有两个孩子 则按照优先级查找删除*/
if (tree->left_child->prior <= tree->right_child->prior) {
change_right(tree);
erase_T(tree->right_child, n);
} else {
change_left(tree);
erase_T(tree->left_child, n);
}
} else if (tree->left_child) {
/*只有左孩子 相当于右节点prior无穷大*/
change_right(tree);
erase_T(tree->right_child, n);
} else if (tree->right_child) {
/*反之同理*/
change_left(tree);
erase_T(tree->left_child, n);
}
}
总结
treap树是一种随机化数据结构。尽管他不能保证树的绝对平衡(avl树可以),但可以很大程度上避免链的产生。一般可以证明,在数据绝对随机的情况下,普通bst的性能是最好的,而treap尽可能通过调整使树趋于随机构建。treap是在严谨的数学证明上建立的,即他的期望复杂度为O(logn)。另外,treap的编程复杂度是所有自调整bst中最简单的,在OI中有着广泛的应用。
完整代码 对于http://codevs.cn/problem/1164/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <map>
using namespace std;
template <typename T>
class node {
public:
T data;
node *left_child, *right_child;
int prior;
};
template <typename T>
class treap {
private:
node<T> *root;
void change_right(node<T>* &n)
{
/*
* 向右旋转(顺时��?) 把当前节点的左孩子作为父节点.
*/
node<T> *temp = n->left_child;
n->left_child = temp->right_child;
temp->right_child = n;
n = temp;
temp = 0;
}
void change_left (node<T>* &n)
{
/*
* 向左旋转 与向右相��?
*/
node<T> *temp = n->right_child;
n->right_child = temp->left_child;
temp->left_child = n;
n = temp;
temp = 0;
}
/*私有成员函数*/
/*insert的私有实��?*/
void put (node<T>* &n, T data)
{
/*节点为空,新建并插入数据*/
if (!n) {
n = new node<T>;
n->data = data;
n->left_child = n->right_child = 0;
n->prior = rand()%1000000007;
/*随机数据作为优先��?*/
return;
}
/*否则左右查找*/
if (data == n->data) {
n->data = data;
//cout << data.first << " " << data.second << endl;
} else if (data < n->data) {
put (n->left_child, data);
if (n->left_child->prior < n->prior)
change_right(n);
/*如果左节点优先值比当前��?, 即破坏了小根堆的性质
* 则向右旋转当前节点以维护堆性质
*/
} else {
put (n->right_child, data);
if (n->right_child->prior < n->prior)
change_left(n);
/*与上面刚好相��?*/
}
}
/*find的私有实��?*/
node<T>* get (node<T>* n, T data)
{
if (!n)
return 0;
/*找不��?*/
if (n->data == data)
return n;
else if (data < n->data)
return get(n->left_child, data);
else
return get(n->right_child, data);
/*左右查找*/
}
/*析构函数私有实现*/
void del (node<T> *n)
{
if (n) {
/*删除左右节点*/
del (n->left_child);
del (n->right_child);
delete n;
/*释放父节��?*/
}
}
/*删除某个元素*/
void erase_T(node<T>* &tree, T n)
{
if (!tree)
return;
/*如果不是则按照bst左右查找*/
if (!(tree->data == n)) {
if (n < tree->data)
erase_T(tree->left_child, n);
else
erase_T(tree->right_child, n);
return;
}
/*如果已经没有左右节点, 删除��?*/
if (!tree->left_child && !tree->right_child) {
delete tree;
tree = 0;
} else if (tree->left_child && tree->right_child){
/*如果有两个孩��? 则按照优先级查找删除*/
if (tree->left_child->prior <= tree->right_child->prior) {
change_right(tree);
erase_T(tree->right_child, n);
} else {
change_left(tree);
erase_T(tree->left_child, n);
}
} else if (tree->left_child) {
/*只有左孩��? 相当于右节点prior无穷��?*/
change_right(tree);
erase_T(tree->right_child, n);
} else if (tree->right_child) {
/*反之同理*/
change_left(tree);
erase_T(tree->left_child, n);
}
}
/*dfs私有实现*/
void dfs_lr (node<T>* &n, void (*function)(T t))
{
if (!n) return;
/*如果为空直接退��?*/
dfs_lr(n->left_child, function);
function(n->data);
dfs_lr(n->right_child, function);
/*否则左右遍历*/
}
public:
treap ()
{
root = 0;
srand(time(NULL));
}
~treap()
{
del (root);
/*删除��?*/
}
/*成员函数*/
/*
* 函数��? insert(插入)
* O(log2n) -> O(n)
* 参数说明 data:插入的数��?
* 请重��?"<"以自定义数据类型或降��?(默认升序)
* 例如
* struct data {
* int d;
* friend bool operator < (data a, data b) {
* return a.d > b.d;
* //降序
* }
* }
*/
void insert (T data)
{
put (root, data);
/*调用私有成员函数*/
}
/*
* 函数��? find(查找数据)
* O(log2n) -> O(n)
* 参数说明 data:查找的数��?
* 请重��?"<"以自定义数据类型或降��?(默认升序)
*/
node<T>* find (T data)
{
return get (root, data);
}
/*
* 函数��? empty(判断是否为空��?)
* O(1)
*/
bool empty()
{
return (root == 0);
}
/*
* 函数��? clear(清空)
* O(n)
*/
void clear ()
{
del (root);
}
/*
* 函数��? count(返回data是否存在)
* O(log2n) -> O(n)
*/
bool count(T data)
{
return find(data) != 0;
}
/*
* 函数��? eraze(删除元素)
* O(log2n) -> O(n)
*/
void erase(T data)
{
erase_T(root, data);
}
/*
* 函数��? dfs(深度优先遍历)
* O(n)
* 参数说明 function: 一个函数指��?,进行深度优先遍历会执行function(T), T为遍历到的数��?
*/
void dfs (void (*function)(T t))
{
dfs_lr(root, function);
/*调用私有成员函数*/
}
};
struct p {
int x;
int times;
friend bool operator < (p a, p b)
{
return a.x < b.x;
}
friend bool operator == (p a, p b)
{
return a.x == b.x;
}
};
void out(p a)
{
printf ("%d %d
", a.x, a.times);
}
int main()
{
treap< p > tr;
int n, temp;
scanf ("%d", &n);
for (int i=1; i<=n; i++) {
scanf ("%d", &temp);
p a; a.x = temp;
if (!tr.find(a)) {
a.times = 1;
tr.insert(a);
} else {
a.times = tr.find(a)->data.times + 1;
tr.insert(a);
}
//tr.dfs(out);
}
tr.dfs(out);
return 0;
}