类似avl树的还有红黑树和伸展树,然而编程确实很复杂,我先总结treap树吧,比赛啥的也能用得上。从树堆这个名字不难看出treap这种数据结构应该同时具有二叉搜索树与二叉堆的某些性质,实际上树堆首先是一颗二叉搜索树,也就是说它满足 left < root < right;其次它还有一个优先级域(插入时随机给予的),该优先级域需满足left < root && right < root,也就是所谓的堆序性,由于优先级是随机给予的,这样得到的搜索树平均深度为log(n)(算法导论没看懂,证明不来QAQ),难点在于在插入与删除过程中维护这两个性质。
0.treap树一般结构
struct treap{
treap* left;
treap* right;
int val,priority;//值域,优先级域。
treap(int v,int p):val(v),priority(p),left(NULL),right(NULL){}
};
typedef treap* tree;
1.插入
插入过程中需要用到AVL旋转操作,我们知道旋转不改变二叉搜索树的性质(只改变深度),左单旋与右单旋可以分别把左右儿子提到根节点,我们可以通过该方法来维护堆序性。插入时先按照二叉搜索插入规则插入,然后在回溯过程中,检查子树优先级是否比根节点小,如果比根节点小,就把它旋转到根节点,这样一步步回溯到最后就保证了treap的堆序性。
//由于没有高度,所以旋转时不需要更新高度。
tree LL(tree t){
tree t1 = t->left;
t->left = t1->right;
t1->right = t;
return t1;
}
tree RR(tree t){
tree t1 = t->right;
t->right = t1->left;
t1->left = t;
return t1;
}
tree insert(tree t,int val){
if(t==NULL){
t = new treap(val,rand());//随机生成优先级。
return t;
}
if(val > t->val){
t->right = insert(t->right,val);//往右插
//插入结束检查优先级是否正确。
if(t->right->priority < t->priority){t = RR(t);}
}
else if(val < t->val){
t->left = insert(t->left,val);
if(t->left->priority < t->priority){t = LL(t);}
}
return t;
}
相对avl而言,treap树的删除代码简单多了,没有太多复杂的情形,找到待删除点之后:
(0)该点没有儿子时,直接删掉它。
(1)如果该点只有一个儿子,直接用该儿子代替它即可;
(2)两个儿子时,把优先级较小的儿子旋转到该点,然后继续递归删除该点所在的子树(直到满足情况一)。
tree remove(tree t,int val){
if(t==NULL)return t;
if(val > t->val)t->right = remove(t->right,val);//走右边
else if(val < t->val)t->left = remove(t->left,val);
else{
if(t->left && t->right){//有俩儿子
//左儿子优先级小,把它旋转到根
if(t->left->priority < t->right->priority){
t = LL(t);
t->right = remove(t->right,val);//递归删除
}
else{
t = RR(t);
t->left = remove(t->left,val);
}
}
else{
//如果左子树存在就直接返回它,否则返回右子树。
tree v = t->left ? t->left : t->right;
delete t;
return v;
}
}
return t;
}
总的来说treap树的编程相对与avl树而言是很简单的,性能平均效果也不差,大部分情形都能hold住,不过似乎主要用在比赛上,工程里应用多的还是红黑树这种绝对最坏性能达到log(n)的数据结构,不过作为随机化方法的典型,学习一下还是很有必要的。
4.贴一下整体的代码吧
#include
#include
#include
#include
using namespace std;
struct treap{
treap* left;
treap* right;
int val,priority;
treap(int v,int p):val(v),priority(p),left(NULL),right(NULL){}
};
typedef treap* tree;
//由于没有高度,所以旋转时不需要更新高度。
tree LL(tree t){
tree t1 = t->left;
t->left = t1->right;
t1->right = t;
return t1;
}
tree RR(tree t){
tree t1 = t->right;
t->right = t1->left;
t1->left = t;
return t1;
}
tree insert(tree t,int val){
if(t==NULL){
t = new treap(val,rand());//随机生成优先级。
return t;
}
if(val > t->val){
t->right = insert(t->right,val);//往右插
//插入结束检查优先级是否正确。
if(t->right->priority < t->priority){t = RR(t);}
}
else if(val < t->val){
t->left = insert(t->left,val);
if(t->left->priority < t->priority){t = LL(t);}
}
return t;
}
tree remove(tree t,int val){
if(t==NULL)return t;
if(val > t->val)t->right = remove(t->right,val);//走右边
else if(val < t->val)t->left = remove(t->left,val);
else{
if(t->left && t->right){//有俩儿子
//左儿子优先级小,把它旋转到根
if(t->left->priority < t->right->priority){
t = LL(t);
t->right = remove(t->right,val);//递归删除
}
else{
t = RR(t);
t->left = remove(t->left,val);
}
}
else{
//如果左子树存在就直接返回它,否则返回右子树。
tree v = t->left ? t->left : t->right;
delete t;
return v;
}
}
return t;
}
void travel(tree t){
if(!t)return;
if(t->left)travel(t->left);
cout << t->val << " ";
if(t->right)travel(t->right);
}
void level(tree t){
if(!t)return;
tree now,last=t;
queue qu;
qu.push(t);
while(qu.size()){
now = qu.front();qu.pop();
if(now->left)qu.push(now->left);
if(now->right)qu.push(now->right);
cout << now->val << "(" << now->priority << ")" << " ";
if(now == last && qu.size()){last = qu.back();cout << endl;}
}
cout << endl;
}
int main(){
int a[10] = {1,8,3,0,9,5,6,2,4,7};
tree t = NULL;
int i;
srand(time(0));
for(i=0;i<10;i++){
level(t);cout << "************************\n";
t = insert(t,a[i]);
}
travel(t);cout << endl;
level(t);
t = remove(t,8);
travel(t);cout << endl;
level(t);
}