Treap
Treap是一种动态平衡的BST(Binary Search Tree),它每个节点拥有键值和优先级两种属性。对于键值而言,它是一颗排序二叉树。对于优先级而言,这棵树是堆(优先级最高的是根节点)。可以证明Treap中插入,删除和查找的期望时间复杂度均为O(logn)。关于Treap的更多介绍,可见刘汝佳《训练指南》P230。
一般我们用Treap就是用来替代平衡二叉排序树用的。
Treap实现的代码很少,实现简单。我们可以在Treap的基础上实现名次树。
名次树支持两个操作:
1)找出第k小的元素(元素从小到大排序的第k个)。
2)找到值x的名次。
Treap基本代码:
<span style="font-size:18px;">#include<cstdio> #include<cstring> #include<cstdlib> using namespace std; struct Node { Node *ch[2]; int r;//优先级,构成大顶堆 int v;//键值,构成排序二叉树 int cmp(int x)//比较键值大小 { if(x==v) return -1; return x<v?0:1; } }; //d=0表示左旋,d=1表示右旋 void rotate(Node* &o,int d) { Node *k=o->ch[d^1]; o->ch[d^1]=k->ch[d]; k->ch[d]=o; o=k; } //插入值为x的节点 void insert(Node* &o,int x) { if(o==NULL) { o=new Node(); o->ch[0]=o->ch[1]=NULL; o->v=x; o->r=rand();//在cstdlib头声明 } else { //如这里改成int d=o->cmp(x); //就不可以插入相同的值,因为d可能为-1 int d=x<(o->v)?0:1; insert(o->ch[d],x); if(o->ch[d]->r > o->r) rotate(o,d^1); } } //删除v值为x的节点 void remove(Node *&o,int v) { if(o==NULL) return ;//空时返回 int d=o->cmp(v); if(d==-1)//o就是需要删除的节点 { Node *u=o; if(o->ch[0] && o->ch[1]) { int d2 = o->ch[0]->r < o->ch[1]->r ?0:1; rotate(o,d2); remove(o->ch[d2],v); } else { if(o->ch[0]==NULL)o=o->ch[1]; else o=o->ch[0]; delete u;//记得删除节点 } } else remove(o->ch[d],v); } int find(Node *o,int x) { while(o) { int d=o->cmp(x); if(d==-1)return 1; //存在 o=o->ch[d]; } return 0; //不存在 } </span>
Treap实现名次树的代码:
<span style="font-size:18px;">#include<cstdio> #include<cstring> #include<cstdlib> #include<cassert> using namespace std; struct Node { Node *ch[2]; int r,v,s;//s表示节点数 Node(int v):v(v) { ch[0]=ch[1]=NULL; r=rand();//在cstdlib头声明 s=1; } int cmp(int x) { if(x==v)return -1; return x<v?0:1; } void maintain() { s=1; if(ch[0]!=NULL) s+=ch[0]->s; if(ch[1]!=NULL) s+=ch[1]->s; } }; void rotate(Node* &o,int d) { Node *k=o->ch[d^1]; o->ch[d^1]=k->ch[d]; k->ch[d]=o; o->maintain(); k->maintain(); o=k; } void insert(Node* &o,int x)//o子树中事先不存在x { if(o==NULL) o=new Node(x); else { //如这里改成int d=o->cmp(x); //就不可以插入相同的值,因为d可能为-1 int d=x<(o->v)?0:1; insert(o->ch[d],x); if(o->ch[d]->r > o->r) rotate(o,d^1); } o->maintain(); } void remove(Node* &o,int x) { if(o==NULL) return ;//空时返回 int d=o->cmp(x); if(d==-1) { Node *u=o; if(o->ch[0] && o->ch[1]) { int d2=(o->ch[0]->r < o->ch[1]->r)?0:1; rotate(o,d2); remove(o->ch[d2],x); } else { if(o->ch[0]==NULL) o=o->ch[1]; else o=o->ch[0]; delete u;//这个要放里面 } } else remove(o->ch[d],x); if(o) o->maintain();//之前o存在,但是删除节点后o可能就是空NULL了,所以需要先判断o是否为空 } //返回关键字从小到大排序时的第k个值 int kth(Node* o,int k) { assert(o && k>=1 && k<=o->s);//保证输入合法 int s=(o->ch[0]==NULL)?0:o->ch[0]->s; if(k==s+1) return o->v; else if(k<=s) return kth(o->ch[0],k); else return kth(o->ch[1],k-s-1); } //返回值x在树中的排名,就算x不在o树中也能返回排名 //返回值范围在[1,o->s+1]范围内 int rank(Node* o,int x) { if(o==NULL) return 1;//未找到x; int num= o->ch[0]==NULL ? 0:o->ch[0]->s; if(x==o->v) return num+1; else if(x < o->v) return rank(o->ch[0],x); else return rank(o->ch[1],x)+num+1; } int main() { int n=0; while(scanf("%d",&n)==1 && n) { Node *root=NULL; for(int i=0;i<n;i++) { int x; scanf("%d",&x); if(root==NULL) root=new Node(x); else insert(root,x); } int v; while(scanf("%d",&v)==1) { printf("%d\n",rank(root,v)); } } return 0; } </span>
Treap应用
POJ 1442 Black Box(Treap):Treap名次树应用。解题报告!
POJ 3481 Double Queue(Treap):平衡二叉树基本应用。解题报告!
POJ 2761 Feed the dogs(Treap名次树+离线处理):如何解决区间第k大的数的询问?解题报告!
POJ 2985 The k-th LargestGroup(Treap+并查集):同时用并查集且维护名次树。解题报告!
原文地址:http://blog.csdn.net/u013480600/article/details/44779293