Treap 代码实现及其原理
Treap是一颗拥有键值,优先级两种权值的树.
对于键值而言,这棵树是排序二叉树(BST Balance Sort Tree);
对于优先级而言,这棵树是堆,既在这棵树的任意子树中,根节点的优先级是最大的(这个性质称为堆性质)
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 using namespace std; 6 struct Node{ 7 Node *ch[2]; //左右子树 8 int r; // 优先级,数值越大,优先级越高 9 int v; //值 10 bool operator < (const Node &rhs ) const{ 11 return r < rhs.r; //根据优先级比较节点 12 } 13 int cmp(int x) const{ 14 if( x == v ) return -1; 15 return x < v ? 0 : 1; 16 } 17 }; 18 // 注意代码中的小技巧 19 // 因为 d = 0 | 1, d^1 = 1-d, 但计算速度更快。 20 // 另一个关键之处在于o的类型。 21 // 首先,o是指向一个Treap节点的指针 22 // 因此为Node *类型,其次,O本身是可以修改的 23 // 因此c是引用。若非如此,旋转后指针指向将不改变 24 void rotate( Node* &o, int d ){ 25 // d = 0: 左旋, d = 1: 右旋 26 Node *k = o->ch[d^1]; 27 o->ch[d^1] = k->ch[d]; 28 k->ch[d] = o; 29 o = k; 30 } 31 // 插入节点时,首先随即给新节点一个优先级 32 // 然后执行普通的插入算法.(根据键值大小判断在哪颗子树上) 33 // 执行完毕后用左右旋让这个节点往上走,从而维持堆性质. 34 35 // Treap的插入操作分为 BST插入 与 旋转 两个部分. 36 // 若采用递归实现,则只需在递归插入后判断是否需要旋转. 37 38 // 在以 o 为根的子树中插入键值x,修改o 39 void insert( Node* &o, int x ){ 40 if( o == NULL ){ 41 o = new Node(); 42 o->ch[0] = o->ch[1] = NULL; 43 o->v = x; o->r = rand(); 44 } 45 else{ // 旋转方向与插入位置相反 46 int d = o.cmp(x); 47 insert( o->ch[d], x ); 48 if( o->ch[d]->r > o->r ) 49 rotate( o, d^1 ); 50 } 51 } 52 53 // 删除时,首先找到待删除节点. 54 // 如果它只有一颗子树,情况就简单了. 55 // 直接用这颗子树代替这个待删除节点成为根即可.(注意o是叶子节点也符合这个条件) 56 // 麻烦的是 o有两棵子树的情况. 57 // 我们先把这两棵子树中优先级高的一颗旋转到根. 58 // 然后递归在另一棵子树中删除节点o. 59 // 比如,如果左子节点的优先级比较高,就必须右旋,否则会违反堆性质. 60 // 如果右子节点优先级高,就必须左旋.代码如下. 61 void remove( Node* &o, int x ){ 62 int d = o->cmp[x]; 63 if( d == -1 ){ 64 if( o->ch[0] == NULL ) o = o->ch[1]; 65 else if( o->ch[1] == NULL ) o = o->ch[0]; 66 else{ 67 int d2 = (o->ch[0]->r > o->ch[1]->r) ? 1 : 0; //旋转方向与优先级关系相反 68 retate( o, d2 ); remove( o->ch[d2], x ); 69 } 70 } 71 else remove( o->ch[d], x ); 72 } 73 // 有意思的是,删除和插入是对称的. 74 // 事实上,前面的那个例子从前往后看是插入,从后往前看是删除. 75 // 二者的共同特点是'只有一种旋转方法' 76 77 // 上面的insert与remove 都没有处理 78 // 待插入值已经存在, 待删除值不存在 79 // 因此在调用相应函数之前请进行一次查找. 80 int find( Node *o, int x ){ 81 while( o != NULL ){ 82 int d = o->cmp(x); 83 if( d == -1 ) return 1; 84 else o = o->ch[d]; 85 } 86 return 0; 87 }