2018年8月9號(treap)

  已經兩個月沒有寫博客了,今天忽然想寫了(實際上是在補前的)我一直沒有看懂treap;如今有點感受了,就準備寫一篇論文,可能有點low;node

  首先,要知道二叉查找樹是啥,這就要請度娘:二叉查找樹  百度百科
c++

  明白二叉查找樹就行;函數

接着開始騷操做:  平衡樹有7大騷操做: 1.treap旋轉(zig/zag)操做    2.treap插入操做    3.treap刪除操做    4.求一個元素在treap的前驅    5.求一個元素在treap的後繼spa

6.在treap中查找排名第K的元素    7.在treap中求元素的排名 code

就這樣就從第一個開始講起:blog

首先我要先然你們看懂個人結構體:排序

 

1 struct node{ 2     int l;           //左子節點
3     int r;          //右子節點
4     int pri;       //每一個節點的權值
5     int key;     //優先級
6     int sze;     //以x爲根的左子節點的個數
7     int same; //記錄重複節點的個數
8 }e[N*2];

 

 

 

1.treap(zig/zag)遞歸

  首先一棵treap樹要保存一個最小堆的性質而這個最小堆性質就有隨機生成的key來維護:get

圖中的紅色部分就是隨機生成的key他要符合最小堆的性質,可是插入一個數他可能會不符合最小堆的性質時就要用到左轉右轉來維護這個最小堆;博客

如:左圖他就破壞了堆的性質就要把它左右旋,圖中將12進行右旋獲得一個符合條件的樹;

  zig:

 1 void zig(int &k)                    //這裏必需要加上一個&符號處理父子關係
 2 {  3     int y=e[k].l;                //選取當前結點的左子節點
 4     e[k].l=e[y].r;  5     e[y].r=k;                    //交換y,k的父子位置,維護BST性質
 6     e[y].sze=e[k].sze;      //維護子樹的size
 7     upt(k);                      //更新節點k的size
 8     k=y;  9 } 10 //BST就是二叉搜索樹

  zag:

和上面同樣操做

 1 void zag(int &k)  2 {  3     int y=e[k].r;  4     e[k].r=e[y].l;  5     e[y].l=k;  6     e[y].sze=e[k].sze;  7  upt(k);  8     k=y;  9 } 10 和上方的zig的操做同樣,就很少說了(就是左改爲了右)

2.treap插入操做

   

   給節點隨機分配一個優先級,先和二叉排序樹的插入同樣,先把要插入的點插入到一個葉子上,而後跟維護堆同樣,若是當前節點的優先級比根大就旋轉,若是當前節點是跟的左兒子就右旋若是當前節點是跟個右兒子就左旋。

  代碼實現:

 1 void Insert(int &k,const int &pre)  2 {  3     if(!k)  4  {  5         k=++pool;e[k].pri=pre;e[k].key=rand();    //rand()是隨機函數就是講樹節點的key隨機生成
 6         e[k].same=e[k].sze=1;e[k].l=e[k].r=0;    //新建節點
 7         return ;  8  }  9     else    ++e[k].sze; 10     if(e[k].pri==pre)    ++e[k].same;                //重複節點的處理
11     else if(pre<e[k].pri) 12  { 13  Insert(e[k].l,pre); 14         if(e[e[k].l].key<e[k].key)                      //維護堆的性質
15  zig(k); 16  } 17     else
18  { 19  Insert(e[k].r,pre); 20         if(e[e[k].r].key<e[k].key)                     //同上
21  zag(k); 22  } 23     return ; 24 }

3.treap刪除操做

  有了旋轉的操做以後,Treap的刪除比二叉排序樹還要簡單。由於Treap知足堆性質,因此咱們只須要把要刪除的節點旋轉到葉節點上,而後直接刪除就能夠了。具體的方法就是每次找到優先級最大的兒子,向與其相反的方向旋轉,直到那個節點被旋轉到葉節點,而後直接刪除。

代碼:

 1 void Delete(int &k,const int &pre)  2 {  3     if(e[k].pri==pre)  4  {  5         if(e[k].same>1)  6  {  7             --e[k].same;  8             --e[k].sze;  9  } 10         else if(!e[k].l||!e[k].r) 11         k=e[k].l+e[k].r; 12         else if(e[e[k].l].key<e[e[k].r].key) 13  { 14  zig(k); 15  Delete(k,pre); 16  } 17         else
18  { 19  zag(k); 20  Delete(k,pre); 21  } 22         return ; 23  } 24     --e[k].sze; 25     if(pre<e[k].pri) 26  Delete(e[k].l,pre); 27     else
28  Delete(e[k].r,pre); 29     return ; 30 }

4.求一個元素在treap的前驅

 

1.從根節點開始訪問,初始化最優節點爲空節點;

2.若是當前節點的值不大於要求前驅的元素的值,更新最優節點爲當前節點,訪問當前節點的右子節點;

 

3.若是當前節點的值大於要求前驅的元素的值,訪問當前節點的左子節點;

 

4.若是當前節點是空節點,查找結束,最優節點就是要求的前驅。

代碼:

 1 int qianqu(const int &key)  2 {  3     int x=rt,res=-INF;  4     while(x)  5  {  6         if(e[x].pri<key)  7  {  8             res=e[x].pri;  9             x=e[x].r;//要求的前驅爲節點x或在節點x的右節點x的右子樹內
10  } 11         else
12         x=e[x].l; 13  } 14     return res; 15 }

 5.求一個元素在treap的後繼

  和前驅的基本同樣:

 1 int houji(const int &key)  2 {  3     int x=rt,res=INF;  4     while(x)  5  {  6         if(e[x].pri>key)  7  {  8             res=e[x].pri;  9             x=e[x].l; 10  } 11         else
12         x=e[x].r; 13  } 14     return res; 15 }

6.在treap中查找排名第K的元素

若是咱們想查找第k小的元素或者詢問某個元素在Treap中從小到大的排名時,咱們就必須知道每一個子樹中節點的個數。咱們稱以一個子樹的全部節點的權值之和,爲子樹的大小。因爲插入、刪除、旋轉等操做,會使每一個子樹的大小改變,因此咱們必須對子樹的大小進行動態的維護。

對於旋轉,咱們要在旋轉後對子節點和根節點分別從新計算其子樹的大小。

對於插入,新創建的節點的子樹大小爲1。在尋找插入的位置時,每通過一個節點,都要先使以它爲根的子樹的大小增長1,再遞歸進入子樹查找。

對於刪除,在尋找待刪除節點,遞歸返回時要把全部的通過的節點的子樹的大小減小1。要注意的是,刪除以前必定要保證待刪除節點存在於Treap中。

 1 int di(int &k)  2 {  3     int x=rt;  4     while(x)  5  {  6         if(e[e[x].l].sze<k&&e[e[x].l].sze+e[x].same>=k)  7         return e[x].pri;  8         if(e[e[x].l].sze>=k)    x=e[x].l;  9         else
10  { 11             k-=e[e[x].l].sze+e[x].same; 12             x=e[x].r; 13  } 14  } 15     return 0; 16 }

7.在treap中求元素的排名 

  

 1 int piming(const int &key)  2 {  3     int x=rt,res=0;  4     while(x)  5  {  6         if(key==e[x].pri)  7         return res+e[e[x].l].sze+1;  8         if(key<e[x].pri)    x=e[x].l;  9         else
10  { 11             res+=e[e[x].l].sze+e[x].same; 12             x=e[x].r; 13  } 14  } 15     return res; 16 }

這個應該是容易操做的;

最後貼一整個代碼:

 1 #include<bits/stdc++.h>
 2 #define INF 0x7fffff
 3 #define N 100010
 4 using namespace std;  5 struct node{  6     int l;  7     int r;  8     int pri;  9     int key;  10     int sze;  11     int same;  12 }e[N*2];  13 int rt,pool,n;  14 void upt(const int &k)  15 {  16     e[k].sze=e[e[k].l].sze+e[e[k].r].sze+e[k].same;  17 }  18 void zig(int &k)  19 {  20     int y=e[k].l;  21     e[k].l=e[y].r;  22     e[y].r=k;  23     e[y].sze=e[k].sze;  24  upt(k);  25     k=y;  26 }  27 void zag(int &k)  28 {  29     int y=e[k].r;  30     e[k].r=e[y].l;  31     e[y].l=k;  32     e[y].sze=e[k].sze;  33  upt(k);  34     k=y;  35 }  36 void Insert(int &k,const int &pre)  37 {  38     if(!k)  39  {  40         k=++pool;e[k].pri=pre;e[k].key=rand();  41         e[k].same=e[k].sze=1;e[k].l=e[k].r=0;  42         return ;  43  }  44     else    ++e[k].sze;  45     if(e[k].pri==pre)    ++e[k].same;  46     else if(pre<e[k].pri)  47  {  48  Insert(e[k].l,pre);  49         if(e[e[k].l].key<e[k].key)  50  zig(k);  51  }  52     else
 53  {  54  Insert(e[k].r,pre);  55         if(e[e[k].r].key<e[k].key)  56  zag(k);  57  }  58     return ;  59 }  60 void Delete(int &k,const int &pre)  61 {  62     if(e[k].pri==pre)  63  {  64         if(e[k].same>1)  65  {  66             --e[k].same;  67             --e[k].sze;  68  }  69         else if(!e[k].l||!e[k].r)  70         k=e[k].l+e[k].r;  71         else if(e[e[k].l].key<e[e[k].r].key)  72  {  73  zig(k);  74  Delete(k,pre);  75  }  76         else
 77  {  78  zag(k);  79  Delete(k,pre);  80  }  81         return ;  82  }  83     --e[k].sze;  84     if(pre<e[k].pri)  85  Delete(e[k].l,pre);  86     else
 87  Delete(e[k].r,pre);  88     return ;  89 }  90 int qianqu(const int &key)  91 {  92     int x=rt,res=-INF;  93     while(x)  94  {  95         if(e[x].pri<key)  96  {  97             res=e[x].pri;  98             x=e[x].r;  99  } 100         else
101         x=e[x].l; 102  } 103     return res; 104 } 105 int houji(const int &key) 106 { 107     int x=rt,res=INF; 108     while(x) 109  { 110         if(e[x].pri>key) 111  { 112             res=e[x].pri; 113             x=e[x].l; 114  } 115         else
116         x=e[x].r; 117  } 118     return res; 119 } 120 int di(int &k) 121 { 122     int x=rt; 123     while(x) 124  { 125         if(e[e[x].l].sze<k&&e[e[x].l].sze+e[x].same>=k) 126         return e[x].pri; 127         if(e[e[x].l].sze>=k)    x=e[x].l; 128         else
129  { 130             k-=e[e[x].l].sze+e[x].same; 131             x=e[x].r; 132  } 133  } 134     return 0; 135 } 136 int piming(const int &key) 137 { 138     int x=rt,res=0; 139     while(x) 140  { 141         if(key==e[x].pri) 142         return res+e[e[x].l].sze+1; 143         if(key<e[x].pri)    x=e[x].l; 144         else
145  { 146             res+=e[e[x].l].sze+e[x].same; 147             x=e[x].r; 148  } 149  } 150     return res; 151 } 152 int main() 153 { 154     scanf("%d",&n); 155     for(int i=1;i<=n;i++) 156  { 157         int opt,x; 158         scanf("%d%d",&opt,&x); 159         if(opt==1) 160  Insert(rt,x); 161         if(opt==2) 162  Delete(rt,x); 163         if(opt==3) 164         printf("%d\n",piming(x)); 165         if(opt==4) 166         printf("%d\n",di(x)); 167         if(opt==5) 168         printf("%d\n",qianqu(x)); 169         if(opt==6) 170         printf("%d\n",houji(x)); 171  } 172     return 0; 173 }
相關文章
相關標籤/搜索