treap是二叉搜索树的一个改进版,通过在二叉搜索树(BST)中增添优先权信息,使得BST具备堆的性质:树根节点的优先权最大(或最小)。
简言之,treap = tree + heap
解释完了treap是什么,我们再说一下为什么这么做,其实这个应该先讲,否则会让人迷惑。
这么做的原因是防止BST的退化,在向BST中添加节点时,由于输入数据的随机性,极端情况下可能会导致生成的BST变成一个链表树了,这样BST的优势消失了。为了防止这种情况发生,有多种办法(参见Donald.E.Knuth的《The art of computer programming. Volume 3》),AVL树,Red-black树,treap都是解决这个问题所提出的数据结构。
AVL树的实现较复杂,其结点旋转有两种情况,这里不详细阐述。
treap在每个结点添加的时候随机生成一个prior信息赋予该结点,然后通过结点旋转维持整个树为一个堆(最大堆或者最小堆),这样可以在一定概率上保证整棵树不会严重退化。
关于treap的效率,网上有很多评测。
关于treap的讲解,MIT的《算法导论》里面P265有关于随机建立二叉搜索树的讲解。
treap优点在于实现简单,下面附上一个我实现的c++版本:
#include <stdio.h>
#include <stdlib.h>
#include "../../stack/include/sim_stack.h"
/**********************************************************************
* starstarstarpku@gmail.com
* Binary Search Tree: BST
* Heap; Maximum Heap or Minimum Heap
*
* insert
* search
* delete
*
* left_rotate
* right_rotate
*
* f b: l_rotate f b
* / / ---------------> / / ---> /
* x y x b f
* / / // // / / /
* z m n b z m y x y
* / // /
* n z m n
*
* ******************************************************************/
typedef int treap_key_type;
inline int compare_treap_key (treap_key_type key1, treap_key_type key2)
{
return key1 - key2;
}
typedef struct treap_node {
struct treap_node* left;
struct treap_node* right;
signed int prior;
void* data;
treap_key_type key;
}treap_node;
typedef treap_node* p_treap_node;
/***************************************************
* search key in a treap tree
* return node pointer in tree, or NULL if not found
* ************************************************/
p_treap_node search_treap (p_treap_node root, treap_key_type key)
{
while (root && compare_treap_key(key, root->key)) {
if (compare_treap_key(key, root->key) < 0)
root = root->left;
else if (compare_treap_key(key, root->key) > 0)
root = root->right;
}
return root;
}
/*********************************************************************
* allocate spaces to a new node, whose key comes from parameter key
* return a node pointer, or NULL if failed
* ******************************************************************/
p_treap_node make_new_node (treap_key_type key, void* data=0) {
p_treap_node p = (p_treap_node)malloc(sizeof(treap_node));
if (p == NULL) { printf("malloc failed/n"); exit(1); }
p->left = NULL;
p->right = NULL;
p->key = key;
p->prior = (double)rand() / 65535; // modify here to control treap's capacity
p->data = data;
return p;
}
/****************************************************
* tricky part: rotate root node to left
* *************************************************/
void left_rotate (p_treap_node& root)
{
p_treap_node tmp_r = root->right;
treap_key_type tmp_key = root->key;
signed int tmp_prior = root->prior;
root->key = tmp_r->key;
root->prior = tmp_r->prior;
root->right = tmp_r->right;
tmp_r->right = tmp_r->left;
tmp_r->left = root->left;
tmp_r->key = tmp_key;
tmp_r->prior = tmp_prior;
root->left = tmp_r;
}
/****************************************************
* as well as the above function
* tricky part: rotate root node to right
* *************************************************/
void right_rotate (p_treap_node& root)
{
p_treap_node tmp_l = root->left;
treap_key_type tmp_key = root->key;
signed int tmp_prior = root->prior;
root->key = tmp_l->key;
root->prior = tmp_l->prior;
root->left = tmp_l->left;
tmp_l->left = tmp_l->right;
tmp_l->right = root->right;
tmp_l->key = tmp_key;
tmp_l->prior = tmp_prior;
root->right = tmp_l;
}
/****************************************************
* insert key in a treap tree
* return node pointer of new node, or NULL if failed
* *************************************************/
void insert_treap (p_treap_node& root, treap_key_type key, void* data=0)
{
if (!root) {
root = make_new_node (key, data);
return;
}
if (compare_treap_key(key, root->key) > 0) {
// insert into root's right subtree
if (root->right != NULL) {
insert_treap(root->right, key, data);
}
else {
root->right = make_new_node(key, data);
}
if (root->prior < root->right->prior) { left_rotate(root); }
}
else if (compare_treap_key(key, root->key) < 0) {
// insert into root's left subtree
if (root->left != NULL) {
insert_treap(root->left, key, data);
}
else {
root->left = make_new_node(key, data);
}
if (root->prior < root->left->prior) { right_rotate(root); }
}
else {
// key already exist in treap tree, do nothing
printf("key: %d already exists in treap/n", key);
}
}
/*******************************************************
* remove node from treap
* return
* ****************************************************/
void remove_treap_node (p_treap_node& root, treap_key_type key)
{
p_treap_node p=root, parent=NULL;
while (p && compare_treap_key(key, p->key)) {
parent = p;
if (compare_treap_key(key, p->key) < 0)
p = p->left;
else if (compare_treap_key(key, p->key) > 0)
p = p->right;
}
if (p == NULL)
return;
while (p->left || p->right) {
// we need to record p's parent, in order to later delete his child pointer
parent = p;
// find out which subtree has higher prior
signed int lp = -100000, rp = -100000;
if (p->left) lp = p->left->prior;
if (p->right) rp = p->right->prior;
if (lp > rp) {
// left subtree has higher prior than right subtree, so
// right_rotate
right_rotate (p);
p = p->right;
}
else {
// as above, right subtree has higher prior than left subtree, so
// left_rotate
left_rotate (p);
p = p->left;
}
}
if (parent && parent->right == p)
parent->right = NULL;
else if (parent && parent->left == p)
parent->left = NULL;
/*if (p->data)
free (p->data);
*/
free(p);
p = NULL;
return;
}
简言之,treap = tree + heap
解释完了treap是什么,我们再说一下为什么这么做,其实这个应该先讲,否则会让人迷惑。
这么做的原因是防止BST的退化,在向BST中添加节点时,由于输入数据的随机性,极端情况下可能会导致生成的BST变成一个链表树了,这样BST的优势消失了。为了防止这种情况发生,有多种办法(参见Donald.E.Knuth的《The art of computer programming. Volume 3》),AVL树,Red-black树,treap都是解决这个问题所提出的数据结构。
AVL树的实现较复杂,其结点旋转有两种情况,这里不详细阐述。
treap在每个结点添加的时候随机生成一个prior信息赋予该结点,然后通过结点旋转维持整个树为一个堆(最大堆或者最小堆),这样可以在一定概率上保证整棵树不会严重退化。
关于treap的效率,网上有很多评测。
关于treap的讲解,MIT的《算法导论》里面P265有关于随机建立二叉搜索树的讲解。
treap优点在于实现简单,下面附上一个我实现的c++版本:
#include <stdio.h>
#include <stdlib.h>
#include "../../stack/include/sim_stack.h"
/**********************************************************************
* starstarstarpku@gmail.com
* Binary Search Tree: BST
* Heap; Maximum Heap or Minimum Heap
*
* insert
* search
* delete
*
* left_rotate
* right_rotate
*
* f b: l_rotate f b
* / / ---------------> / / ---> /
* x y x b f
* / / // // / / /
* z m n b z m y x y
* / // /
* n z m n
*
* ******************************************************************/
typedef int treap_key_type;
inline int compare_treap_key (treap_key_type key1, treap_key_type key2)
{
return key1 - key2;
}
typedef struct treap_node {
struct treap_node* left;
struct treap_node* right;
signed int prior;
void* data;
treap_key_type key;
}treap_node;
typedef treap_node* p_treap_node;
/***************************************************
* search key in a treap tree
* return node pointer in tree, or NULL if not found
* ************************************************/
p_treap_node search_treap (p_treap_node root, treap_key_type key)
{
while (root && compare_treap_key(key, root->key)) {
if (compare_treap_key(key, root->key) < 0)
root = root->left;
else if (compare_treap_key(key, root->key) > 0)
root = root->right;
}
return root;
}
/*********************************************************************
* allocate spaces to a new node, whose key comes from parameter key
* return a node pointer, or NULL if failed
* ******************************************************************/
p_treap_node make_new_node (treap_key_type key, void* data=0) {
p_treap_node p = (p_treap_node)malloc(sizeof(treap_node));
if (p == NULL) { printf("malloc failed/n"); exit(1); }
p->left = NULL;
p->right = NULL;
p->key = key;
p->prior = (double)rand() / 65535; // modify here to control treap's capacity
p->data = data;
return p;
}
/****************************************************
* tricky part: rotate root node to left
* *************************************************/
void left_rotate (p_treap_node& root)
{
p_treap_node tmp_r = root->right;
treap_key_type tmp_key = root->key;
signed int tmp_prior = root->prior;
root->key = tmp_r->key;
root->prior = tmp_r->prior;
root->right = tmp_r->right;
tmp_r->right = tmp_r->left;
tmp_r->left = root->left;
tmp_r->key = tmp_key;
tmp_r->prior = tmp_prior;
root->left = tmp_r;
}
/****************************************************
* as well as the above function
* tricky part: rotate root node to right
* *************************************************/
void right_rotate (p_treap_node& root)
{
p_treap_node tmp_l = root->left;
treap_key_type tmp_key = root->key;
signed int tmp_prior = root->prior;
root->key = tmp_l->key;
root->prior = tmp_l->prior;
root->left = tmp_l->left;
tmp_l->left = tmp_l->right;
tmp_l->right = root->right;
tmp_l->key = tmp_key;
tmp_l->prior = tmp_prior;
root->right = tmp_l;
}
/****************************************************
* insert key in a treap tree
* return node pointer of new node, or NULL if failed
* *************************************************/
void insert_treap (p_treap_node& root, treap_key_type key, void* data=0)
{
if (!root) {
root = make_new_node (key, data);
return;
}
if (compare_treap_key(key, root->key) > 0) {
// insert into root's right subtree
if (root->right != NULL) {
insert_treap(root->right, key, data);
}
else {
root->right = make_new_node(key, data);
}
if (root->prior < root->right->prior) { left_rotate(root); }
}
else if (compare_treap_key(key, root->key) < 0) {
// insert into root's left subtree
if (root->left != NULL) {
insert_treap(root->left, key, data);
}
else {
root->left = make_new_node(key, data);
}
if (root->prior < root->left->prior) { right_rotate(root); }
}
else {
// key already exist in treap tree, do nothing
printf("key: %d already exists in treap/n", key);
}
}
/*******************************************************
* remove node from treap
* return
* ****************************************************/
void remove_treap_node (p_treap_node& root, treap_key_type key)
{
p_treap_node p=root, parent=NULL;
while (p && compare_treap_key(key, p->key)) {
parent = p;
if (compare_treap_key(key, p->key) < 0)
p = p->left;
else if (compare_treap_key(key, p->key) > 0)
p = p->right;
}
if (p == NULL)
return;
while (p->left || p->right) {
// we need to record p's parent, in order to later delete his child pointer
parent = p;
// find out which subtree has higher prior
signed int lp = -100000, rp = -100000;
if (p->left) lp = p->left->prior;
if (p->right) rp = p->right->prior;
if (lp > rp) {
// left subtree has higher prior than right subtree, so
// right_rotate
right_rotate (p);
p = p->right;
}
else {
// as above, right subtree has higher prior than left subtree, so
// left_rotate
left_rotate (p);
p = p->left;
}
}
if (parent && parent->right == p)
parent->right = NULL;
else if (parent && parent->left == p)
parent->left = NULL;
/*if (p->data)
free (p->data);
*/
free(p);
p = NULL;
return;
}