首页 > 代码库 > fhqtreap的学习笔记
fhqtreap的学习笔记
一段时间之前学的fhqtreap……今天闲的没事稍微总结一下吧……学了fhq之后就再也不想写splay了。
fhqtreap的优点:速度快,代码简单,可以进行区间操作,而且也可以进行可持久化。
//学习这个首先得懂平衡树和可并堆。
首先要明确一点:fhqtreap本质上还是一个treap,也就是说它也是靠堆权值来维护树的平衡特性的。但是fhqtreap没有旋转,只有两个最简单的操作split(分裂)和merge(合并),通过这两个操作来维护平衡树的性质。我们用w表示平衡树里存的权值,f表示堆权值。
1.split(root,v,k1,k2)
这个操作是将一个根是root的树分裂成两个部分,将权值小于v的放在一棵平衡树里,大于w的放在另一颗平衡树里,最后返回的k1,k2值就是这两颗树的根。
下面我们考虑如何去划分。注意这里的k1,k2都是变参,k1表示小于v的树,k2表示大于v的树。
假使当前的节点是i,那么我们比较t[i].w和v的大小,如果t[i].w<=v,那么我们可以肯定,i的左子树也都小于v,但是对于右子树我们还不确定。这样的话,令k1=i,然后split(t[i].r,v,t[i].r,k2),相当于把i的右子树砍下来,递归处理i的右子树,拆分右子树,但是这时候k1的位置已经被i给占了,那么如果再有小于v的,我们就直接接到i的右孩子上(因为反正还是在一颗树里),这样满足二叉排序树的性质。同时我们是自顶而下拆、拼接的,所以不用考虑f也满足堆性质
如果t[i].w>v,同理,k2=i,split(t[i].l,v,k1,t[i].l)
这样我们调用split(root,v,k1,k2),就可以把一棵树拆成两个,类似于splay把分界点旋转到根一样,这样我们就可以在k1中寻找大于w的节点得信息,在k2中寻找小于w的节点的信息。
2.root=merge(k1,k2)
这个操作的意思是,把k1,k2两颗树变成一颗,并返回根节点的编号,注意这里k1的所有w都小于k2,因为这个操作通常是我们split然后处理好信息之后再把弄回一棵树。
学过可并堆应该就很好理解了。以为两棵树大小关系确定,我们只要考虑他们的堆权值。这里默认为大根堆。
对于t[k1].f>t[k2].f,t[k1].r=merge(t[k1].r,k2);return k1;
对于t[k1].f<t[k2].f,t[k2].l=merge(k1,t[k2].l);return k2;
注意我们要保证每次递归处理时两颗子树的大小关系不变,所以顺序要注意。
下面我们就可以通过这两个基本的东西实现各种各样的操作了。
3.insert(v)
插入一个权值为v的点,把树按照v的权值成两个,再按照顺序合并回去。
//也有效率更高的插入方法,具体看代码
4.del(v)
删除权值为v的点,把树按照v分成两个k1,k2,再把k2按照v+1分成k3,k4,merge(merge(k1,t[k3].l),merge(t[k3].r,k4))
5.查询前驱后继
split(root,v,k1,k2),分别在k1,k2里面找最大值最小值
6.查询排名
split(root,v,k1,k2),排名是k1的size
7.区间操作
splite(root,l-1,k1,k2);splite(k2,r,k3,k4);k3为操作区间。
下面就是那个经典的普通平衡树的模板,稍稍有点不一样的是我把权值和划分值相等的的放在了第二颗树里
1 /************************************************************** 2 Problem: 3224 3 User: 1090900715 4 Language: Pascal 5 Result: Accepted 6 Time:800 ms 7 Memory:2180 kb 8 ****************************************************************/ 9 10 program j01; 11 type xx=record l,r,f,w,size:longint; end; 12 var t:array[0..100086]of xx; 13 n,i,op,x,k1,k2,k3,k4,root,tt:longint; 14 15 procedure update(i:longint); 16 begin 17 if i=0 then exit; 18 t[i].size:=t[t[i].l].size+t[t[i].r].size+1; 19 end; 20 21 function merge(x,y:longint):longint; 22 begin 23 if (x=0)or(y=0) then exit(x+y); 24 if t[x].f>t[y].f then 25 begin 26 t[x].r:=merge(t[x].r,y); 27 update(x);exit(x); 28 end else 29 begin 30 t[y].l:=merge(x,t[y].l); 31 update(y);exit(y); 32 end; 33 end; 34 35 procedure splite(i,x:longint;var k1,k2:longint); 36 begin 37 if i=0 then 38 begin 39 k1:=0;k2:=0;exit; 40 end; 41 if t[i].w<x then 42 begin 43 k1:=i;splite(t[i].r,x,t[i].r,k2); 44 end else 45 begin 46 k2:=i;splite(t[i].l,x,k1,t[i].l); 47 end; 48 update(i); 49 end; 50 51 function insert(i,k:longint):longint; 52 begin 53 if i=0 then exit(k); 54 if t[i].f>t[k].f then 55 begin 56 if t[i].w<t[k].w then t[i].r:=insert(t[i].r,k) 57 else t[i].l:=insert(t[i].l,k); 58 update(i);exit(i); 59 end; 60 splite(i,t[k].w,t[k].l,t[k].r);update(k);exit(k); 61 end; 62 63 function find(i,x:longint):longint; 64 begin 65 if t[t[i].l].size+1=x then exit(t[i].w); 66 if t[t[i].l].size>=x then exit(find(t[i].l,x)) 67 else exit(find(t[i].r,x-t[t[i].l].size-1)); 68 end; 69 70 function findpre(i:longint):longint; 71 begin 72 while t[i].r<>0 do i:=t[i].r;exit(t[i].w); 73 end; 74 75 function findnex(i:longint):longint; 76 begin 77 while t[i].l<>0 do i:=t[i].l;exit(t[i].w); 78 end; 79 80 begin 81 readln(n);tt:=0;root:=0; 82 randomize; 83 for i:=1 to n do 84 begin 85 readln(op,x); 86 if op=1 then 87 begin 88 inc(tt); 89 t[tt].w:=x;t[tt].f:=random(maxlongint);t[tt].size:=1; 90 root:=insert(root,tt); 91 end; 92 if op=2 then 93 begin 94 splite(root,x,k1,k2);splite(k2,x+1,k2,k3); 95 root:=merge(merge(k1,t[k2].l),merge(t[k2].r,k3)); 96 end; 97 if op=3 then 98 begin 99 splite(root,x,k1,k2);writeln(t[k1].size+1); 100 root:=merge(k1,k2); 101 end; 102 if op=4 then writeln(find(root,x)); 103 if op=5 then 104 begin 105 splite(root,x,k1,k2);writeln(findpre(k1)); 106 root:=merge(k1,k2); 107 end; 108 if op=6 then 109 begin 110 splite(root,x+1,k1,k2); 111 writeln(findnex(k2)); 112 root:=merge(k1,k2); 113 end; 114 end; 115 end.
fhqtreap的学习笔记