平衡二叉树总结三:treap树(树堆)

  类似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;
}

2.删除

 相对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;
}

3.总结

  总的来说treap树的编程相对与avl树而言是很简单的,性能平均效果也不差,大部分情形都能hold住,不过似乎主要用在比赛上,工程里应用多的还是红黑树这种绝对最坏性能达到log(n)的数据结构,不过作为随机化方法的典型,学习一下还是很有必要的。

4.贴一下整体的代码吧

#include<iostream>
#include<stdlib.h>
#include<time.h>
#include<queue>
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<tree> 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);
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值