1. 程式人生 > >平衡樹Treap模板與原理

平衡樹Treap模板與原理

優化 排名 print 比較 pla ans for 後繼 每一個

這次我們來講一講Treap(splay以後再更)

平衡樹是一種排序二叉樹(或二叉搜索樹),所以排序二叉樹可以迅速地判斷兩個值的大小,當然操作肯定不止那麽多(不然我們還學什麽)。

而平衡樹在排序二叉樹的基礎上主要是增加了一個優化:就是高度較為平衡,並可以動態平衡。

而今天要講的treap就是一種動態平衡的方法。

首先說聲抱歉,因為沒有那麽多的時間,所以無法把左旋和右旋兩種操作具體的講,但是可以看劉汝佳的藍書,講得還是挺清楚的。

現在開始。

treap用的是一種比較玄學的方法,就是將每一個點還附上一個隨機值,然後按照堆的性質,不管大小根堆都是一樣的,所以我們每加入一個值,就利用上浮操作進行旋轉,保持堆的性質,且不改變排序二叉樹的性質。

操作很好理解,就是每次insert時進行判斷就行了,若不滿足,則上浮。

具體的排序二叉樹的操作,我就不再贅述了,如果要學,可以看劉汝佳的藍書(感覺在跟劉汝佳打廣告)。

下面送上代碼。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<ctime>
  6 using namespace std;
  7 int n,root,size,ans;
  8 struct P{
  9
int l,r,sz,key,rd,re;//re為重復次數,key為加入權值 10 }t[1000005]; 11 void update(int k) 12 { 13 t[k].sz=t[t[k].l].sz+t[t[k].r].sz+t[k].re; 14 } 15 void left(int &k)//右旋 16 { 17 int y=t[k].r; 18 t[k].r=t[y].l; 19 t[y].l=k; 20 t[y].sz=t[k].sz; 21 update(k); 22 k=y; 23 } 24
void right(int &k)//左旋 25 { 26 int y=t[k].l; 27 t[k].l=t[y].r; 28 t[y].r=k; 29 t[y].sz=t[k].sz; 30 update(k); 31 k=y; 32 } 33 void init(int &k,int x)//加入操作 34 { 35 if(k==0) 36 { 37 size++; 38 k=size; 39 t[k].sz=1; 40 t[k].re=1; 41 t[k].key=x; 42 t[k].rd=rand(); 43 return; 44 } 45 t[k].sz++; 46 if(t[k].key==x) t[k].re++; 47 else{ 48 if(x>t[k].key) 49 { 50 init(t[k].r,x); 51 if(t[t[k].r].rd<t[k].rd) left(k);//這裏和下面都是判斷是不是滿足堆的性質 52 } 53 if(x<t[k].key) 54 { 55 init(t[k].l,x); 56 if(t[t[k].l].rd<t[k].rd) right(k); 57 } 58 } 59 } 60 void del(int &k,int x) 61 { 62 if(k==0) return; 63 if(t[k].key==x) 64 { 65 if(t[k].re>1) 66 { 67 t[k].re--; 68 t[k].sz--; 69 return; 70 } 71 if(t[k].l*t[k].r==0) k=t[k].l+t[k].r;//k代表指針的移動,所以移動到了左或右兒子 72 else 73 { 74 if(t[t[k].l].rd<t[t[k].r].rd){ 75 right(k); 76 del(k,x); 77 } 78 else{ 79 left(k); 80 del(k,x); 81 } 82 } 83 } 84 else{ 85 if(x>t[k].key) 86 { 87 t[k].sz--; 88 del(t[k].r,x); 89 } 90 else{ 91 t[k].sz--; 92 del(t[k].l,x); 93 } 94 } 95 } 96 int rank1(int k,int x)//找x的排名 97 { 98 if(k==0) return 0; 99 if(t[k].key==x) return t[t[k].l].sz+1; 100 if(x>t[k].key) return t[t[k].l].sz+rank1(t[k].r,x)+t[k].re; 101 if(x<=t[k].key) return rank1(t[k].l,x); 102 } 103 int rank2(int k,int x)//找排名為x的數 104 { 105 if(k==0) return 0; 106 else if(x<=t[t[k].l].sz) return rank2(t[k].l,x); 107 else if(x>t[t[k].l].sz+t[k].re) return rank2(t[k].r,x-t[t[k].l].sz-t[k].re); 108 else return t[k].key; 109 } 110 void pre(int k,int x)//找前驅 111 { 112 if(k==0) return; 113 if(t[k].key<x) 114 { 115 ans=k; 116 pre(t[k].r,x); 117 } 118 else pre(t[k].l,x); 119 } 120 void nxt(int k,int x)//找後繼 121 { 122 if(k==0) return; 123 if(t[k].key>x) 124 { 125 ans=k; 126 nxt(t[k].l,x); 127 } 128 else nxt(t[k].r,x); 129 } 130 int main() 131 { 132 srand(time(0)); 133 scanf("%d",&n); 134 for(int i=1;i<=n;i++) 135 { 136 int num,x; 137 scanf("%d%d",&num,&x); 138 if(num==1) init(root,x); 139 if(num==2) del(root,x); 140 if(num==3) printf("%d\n",rank1(root,x)); 141 if(num==4) printf("%d\n",rank2(root,x)); 142 if(num==5) 143 { 144 pre(root,x); 145 printf("%d\n",t[ans].key); 146 } 147 if(num==6) 148 { 149 nxt(root,x); 150 printf("%d\n",t[ans].key); 151 } 152 } 153 return 0; 154 }

模板題:https://www.luogu.org/problemnew/show/P3369

謝謝大家的觀看。

如有不妥之處,請大家指出。

平衡樹Treap模板與原理