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 }

 

posted @ 2013-04-08 20:23  yefeng1627  阅读(412)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor