平衡樹初階——AVL平衡二叉查找樹+三大平衡樹(Treap + Splay + SBT)模板【超詳解】


平衡樹初階——AVL平衡二叉查找樹

 

一、什么是二叉樹

 

1. 什么是樹。

計算機科學里面的樹本質是一個樹狀圖。樹首先是一個有向無環圖,由根節點指向子結點。但是不嚴格的說,我們也研究無向樹。所謂無向樹就是將有向樹的所有邊看成無向邊形成的樹狀圖。樹是一種遞歸的數據結構,所以我們研究樹也是按照遞歸的方式去研究的。

 

2.什么是二叉樹。

我們給出二叉樹的遞歸定義如下:

(1)空樹是一個二叉樹。

(2)單個節點是一個二叉樹。

(3)如果一棵樹中,以它的左右子節點為根形成的子樹都是二叉樹,那么這棵樹本身也是二叉樹。

 

二、BST

1.什么是二叉排序樹。

二叉排序樹是一種二叉樹,它滿足樹的中序遍歷是有序的。

 

2.BSTBinary Search Tree)。

二叉查找樹是一種最普通的二叉排序樹,一般稱作BST,也稱為B-樹。在這棵樹中,滿足在任意一個子樹中,都滿足左子樹 < 根節點 < 右子樹,即該樹的中序遍歷滿足從小到大排序的結果。

 

3.如何構造一個二叉排序樹?

很顯然,要想構造一個BST,就必須在插入節點時,滿足下面的原則:

(1)如果節點為空,則直接插入到該節點。

(2)如果節點不為空,且要插入的值小於等於當前節點的值,那么則將該節點插入到左子樹當中。

(3)如果節點不為空,且要插入的值大於當前節點的值,那么則將該節點插入到右子樹當中。

 

4.利用BST的性質對一個數組進行剃重。

由於BST有二叉排序樹的性質,我們可以利用這樣的性質對一個待定數組進行剃重。原理很簡單,只需要在插入的時候如果已經發現了相等的節點的話,那么則不進行插入即可。也就是說,只有該樹沒有的節點,我們才進行相應的插入操作。

 

 

 

三、BST的相關操作

 

1.建樹(createTree

BST的建立是基於一個數組進行的,本質上是把數組中的數按順序插入的樹中。可以想象,,每插入一個數,平均時間復雜度為O(logn),所以建樹的平均時間復雜度為O(nlogn)

 

2.查找某一個值dsearchTree

如果我們需要在BST上查找一個值d,那么我們需要從根節點開始,按照下面的思路進行遞歸查詢:

(1)如果當前節點為空,則未找到,返回NULL。

(2)如果當前節點不為空,且當前節點的值等於d,那么則找到,返回當前節點。

(3)如果當前節點不為空,且當前節點的值大於d,那么則遞歸在左子樹中尋找。

(4)如果當前節點不為空,且當前節點的值小於d,那么則遞歸在右子樹中尋找。

可以想象,查找操作的平均時間復雜度為O(logn)

 

3.刪除一個值ddeleteTree

如果我們想要刪除一個值d的節點,那么顯然首先需要找到該節點。如果沒有找到,則刪除操作失敗,如果找到,繼續下面的操作即可:

(1)如果找到的節點的右子樹為空,那么直接用該節點的左節點替換當前節點即可。

(2)如果找到的節點的右子樹不為空,且右子樹的左子樹不為空,則遞歸找該右子樹的左子樹。

(3)如果找到的節點的右子樹不為空,且右子樹的左子樹為空,則:

    ①如果找到的該節點的右節點為空,則返回當前節點,用這個節點去替換需要刪除的點即可。

    ②如果找到的該節點的右子樹不為空,則首先用該右子樹替換找到的節點,在用找到的節點替換需要刪除的節點即可。

顯然,刪除操作的平均時間復雜度為O(logn)

 

四、AVL平衡二叉查找樹

1.什么是平衡二叉樹。

平衡二叉樹是一種二叉排序樹,並且滿足樹中任意一個節點的左右子樹的高度保持平衡。

 

2.什么是AVL

AVL是一種二叉查找樹,並且滿足樹中任意一個節點的左右子樹的高度差的絕對值小於等於1,即保持平衡系數不大於1。AVL也稱B-BST(Balanced - Binary Search Tree),而AVL的名稱只是與這種數據結構的作者有關。

 

3.引例:為什么會產生AVL

我們為什么需要研究AVL,換句話說,為什么我們要重視BST的平衡性能呢?我們看下面的一個例子。

我們用1,2,3,4,5,6,7,8,9來進行建樹。我們發現,這樣建樹的結果如下:

 

 

可以看出,這樣二叉樹實際上退化為了一個鏈表。在最壞的情況下,插入和刪除的時間復雜度都退化為了O(n)

很顯然,樹的平衡性越好,這種退化越不明顯。所以為了保持BST的高效,我們研究AVL是必要的。

 

4.如何保持AVL的平衡?

既然要保持左右子樹高度差小於等於1,那么我們一定需要在節點里面定義一個新的屬性,用來記錄該節點為根的樹的高度。由於建樹的過程是遞歸的,所以樹的高度的更新也是遞歸完成的。通過更新高度,我們就可以知道什么時候左右子樹的高度差大於1了,這個時候產生了失衡。一旦某一個節點開始失衡,那么這個時候必須通過旋轉操作使二叉樹達到一個新的平衡。

 

五、AVL的相關操作

1.旋轉操作(rotateAvl

如果在某一個時刻二叉樹發生了失衡,我們就需要對二叉樹進行相應的旋轉使得二叉樹重新達到平衡。這個旋轉並不是隨意的,我們還要保證BST的基本性質,那就是中序遍歷必須有序才行。

我們總結二叉樹失衡的原因,可以歸納為以下四種情況(其中圓形節點代表失衡有關的節點,方形節點代表子樹)

 

 

歸納后發現,對於情況(1)(2),都是由於左子樹高度大於右子樹高度形成的失衡。對於情況(3)(4)則是相反的情況。且情況(1)(3)互為鏡像,情況(2)(4)互為鏡像。所以我們只以(1)(2)種情況作為例子,(3)(4)情況的道理同出一轍。

 

對於情況(1),左子樹高度大於右子樹高度,而在左子樹中,依然是左子樹高度大於右子樹高度。對於這樣的情況,我們需要以1為根進行右轉(rightRotate),右轉的結果是,2變為新的根,而1則成為2的右節點,2原本的右子樹則成為了1的左子樹,即,如下圖:

 

對於情況(2),左子樹高度大於右子樹高度,而在左子樹中,左子樹高度小於右子樹高度。對於這樣的情況,我們需要首先需要以2(leftRotate)為根進行左轉,左轉的結果是3變為1的左子樹,而2變為3的左子樹,而3原本的左子樹成了2的右子樹。如下圖所示:

 

 

之后就轉化為了情況(1),故只需要在以1為根進行右轉即可。

通過這樣的旋轉操作,AVL重新達到了平衡。

這個旋轉操作的時間復雜度是O(1)的。

 

2.高度更新操作。

由於高度更新是遞歸進行的,所以我們選擇回溯的階段進行高度更新。而一個節點的高度應該是左子樹高度和右子樹高度的最大值再加1。

高度更新在整個AVL中是必要的,不管建樹過程中,還是插入操作,或者是刪除操作中,我們都需要時時刻刻對高度進行更新,只有正確的更新高度,才能判斷二叉樹是否失衡。而一旦失衡,我們就需要進行上述的旋轉操作,這些是相輔相承的。

高度更新的時間復雜度也是O(1)的。

 

3.插入操作(insertAvl

在插入操作中,由於插入的新節點,有可能使原本的二叉樹產生了失衡,故應該進行相應的旋轉操作。故,這樣插入操作能穩定在平均O(logn) 的時間復雜度內。

 

4.刪除操作(deleteAvl

再刪除操作中,由於刪除了節點,也有可能是原本平衡的二叉樹產生失衡,故也應該進行相應的旋轉操作。故,這樣刪除操作能穩定在O(logn) 的時間復雜度內。

六、代碼實現(C/C++)

1.對於節點數據類型的處理:

 1 #define Elem int //這樣Elem就是節點中數據的類型。 

2.節點結構體的二叉鏈表

 1 typedef struct LNode
 2 {
 3     Elem data;   //節點數據域
 4     int height;    //節點為根的樹的高度
 5     struct LNode *left,*right;   //左子樹和右子樹
 6     struct LNode()   //構造函數
 7     {
 8         height=0;
 9         left=right=NULL;
10     }
11 }LNode,*avlTree;
12 //這樣定義LNode是節點的類型,avlTree則是節點的指針類型。

3.右旋轉子操作,返回旋轉之后的根節點指針

 1 avlTree _rightRotate(avlTree &r)
 2 {
 3     int lh=0,rh=0;
 4     avlTree t=r->left;
 5     r->left=t->right;
 6     if(r->left) lh=r->left->height;
 7     if(r->right) rh=r->right->height;
 8     r->height=max(lh,rh)+1;
 9     t->right=r;
10     if(t->left==NULL) t->height=1;
11     else t->height=max(t->left->height,r->height)+1;
12     return t;
13 }

4.左旋轉子操作,返回旋轉之后的根節點指針

 1 avlTree _leftRotate(avlTree &r)
 2 {
 3     int lh=0,rh=0;
 4     avlTree t=r->right;
 5     r->right=t->left;
 6     if(r->left) lh=r->left->height;
 7     if(r->right) rh=r->right->height;
 8     r->height=max(lh,rh)+1;
 9     t->left=r;
10     if(t->right==NULL) t->height=1;
11     t->height=max(t->height,r->height)+1;
12     return t;
13 }

5.旋轉主算法(發生失衡,按照規則進行旋轉操作)

 1 void rotateAvl(avlTree &root)
 2 {
 3     int lh=0,rh=0;
 4     if(root->left) lh=root->left->height;
 5     if(root->right) rh=root->right->height;
 6     root->height=max(lh,rh)+1;
 7     if(abs(lh-rh)>1)
 8     {
 9         avlTree tp;
10         int lp=0,rp=0;
11         if(lh>rh) tp=root->left;
12         else tp=root->right;
13         if(tp->left!=NULL) lp=tp->left->height;
14         if(tp->right!=NULL) rp=tp->right->height;
15         if(lh>rh&&lp<rp) root->left=_leftRotate(tp);
16         if(lh<rh&&lp>rp) root->right=_rightRotate(tp);
17         if(lh>rh) root=_rightRotate(root);
18         else root=_leftRotate(root);
19     }
20 }

6.插入操作,向二叉樹r插入d。這里用sign標記表示是否進行剃重,如果signtrue則剃重,signfalse則表示可重復。

 1 void insertAvl(avlTree &r,Elem d,bool sign)
 2 {
 3     //遞歸出口
 4     if(r==NULL) {
 5         r=new LNode();
 6         r->data=d;
 7         r->height++;
 8         return;
 9     }
10     if(d<=r->data)
11     {
12         if(d==r->data&&sign) return; 
13         insertAvl(r->left,d,sign);
14     }
15     else 
16     {
17         insertAvl(r->right,d,sign);
18     }
19     rotateAvl(r);   //檢驗失衡並進行旋轉
20 }

7. 根據data數組建樹。這里用sign標記表示是否進行剃重,如果signtrue則剃重,signfalse則表示可重復。

1 void createAvl(avlTree &root,Elem data[],int n,bool sign)
2 {
3     int i;
4     root=NULL;
5     for(i=0;i<n;i++)
6     {
7         insertAvl(root,data[i],sign);
8     }
9 }

8.查詢root里面的數據d所在節點,如果查詢成功,則返回該節點。若d數據不存在,則查詢失敗,返回NULL

 1 avlTree searchAvl(avlTree root,Elem d)
 2 {
 3     if(root!=NULL)
 4     {
 5         if(d==root->data) return root;
 6         else if(d<root->data) return searchAvl(root->left,d);
 7         else searchAvl(root->right,d);
 8     }
 9     return NULL;
10 }

9.在刪除中尋找實際需要刪除的點,返回實際點。

 1 avlTree _delete(avlTree &root)
 2 {
 3     avlTree ret=root;
 4     if(root->left) ret=_delete(root->left);
 5     else if(root->right)
 6     { 
 7         ret=root->right;
 8         int t=root->data;
 9         root->data=root->right->data;
10         ret->data=t;
11         root->height=1;
12         root->right=NULL;
13         return ret;
14     }
15     else
16     {
17         root=NULL;
18         return ret;
19     }
20     rotateAvl(root);   //檢驗失衡並進行旋轉
21     return ret;
22 }

10.刪除主操作算法,刪除root上的data數據所在節點

 1 void deleteAvl(avlTree &root,Elem data)
 2 {
 3     avlTree ret;
 4     if(!root) return;
 5     if(root->data!=data)   //未找到該節點,首先尋找該節點
 6     {
 7         if(data<root->data) ret=root->left;
 8         else ret=root->right;
 9         deleteAvl(ret,data);   //遞歸尋找
10     }
11     else     //找到該節點
12     {
13         if(!root->right)   //右子樹為空
14         {
15             root=root->left;
16         }
17         else   //右子樹不為空
18         {
19             avlTree t=_delete(root->right);   //尋找實際刪除的節點
20             root->data=t->data;
21             free(t);
22         }
23     }
24     rotateAvl(root);   //檢驗失衡並進行旋轉
25 }

於是我又找到了一份不錯的模版總結,僅供參考!

Treap樹

核心是 利用隨機數的二叉排序樹的各種操作復雜度平均為O(lgn)

Treap模板:

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <ctime>
  4 #include <iostream>
  5 #include <algorithm>
  6 #include <cstdlib>
  7 #include <cmath>
  8 #include <utility>
  9 #include <vector>
 10 #include <queue>
 11 #include <map>
 12 #include <set>
 13 #define max(x,y) ((x)>(y)?(x):(y))
 14 #define min(x,y) ((x)>(y)?(y):(x))
 15 #define INF 0x3f3f3f3f
 16 #define MAXN 100005
 17 
 18 using namespace std;
 19 
 20 int cnt=1,rt=0; //節點編號從1開始
 21 
 22 struct Tree
 23 {
 24     int key, size, pri, son[2]; //保證父親的pri大於兒子的pri
 25     void set(int x, int y, int z)
 26     {
 27         key=x;
 28         pri=y;
 29         size=z;
 30         son[0]=son[1]=0;
 31     }
 32 }T[MAXN];
 33 
 34 void rotate(int p, int &x)
 35 {
 36     int y=T[x].son[!p];
 37     T[x].size=T[x].size-T[y].size+T[T[y].son[p]].size;
 38     T[x].son[!p]=T[y].son[p];
 39     T[y].size=T[y].size-T[T[y].son[p]].size+T[x].size;
 40     T[y].son[p]=x;
 41     x=y;
 42 }
 43 
 44 void ins(int key, int &x)
 45 {
 46     if(x == 0)
 47         T[x = cnt++].set(key, rand(), 1);
 48     else
 49     {
 50         T[x].size++;
 51         int p=key < T[x].key;
 52         ins(key, T[x].son[!p]);
 53         if(T[x].pri < T[T[x].son[!p]].pri)
 54             rotate(p, x);
 55     }
 56 }
 57 
 58 void del(int key, int &x) //刪除值為key的節點
 59 {
 60     if(T[x].key == key)
 61     {
 62         if(T[x].son[0] && T[x].son[1])
 63         {
 64             int p=T[T[x].son[0]].pri > T[T[x].son[1]].pri;
 65             rotate(p, x);
 66             del(key, T[x].son[p]);
 67         }
 68         else
 69         {
 70             if(!T[x].son[0])
 71                 x=T[x].son[1];
 72             else
 73                 x=T[x].son[0];
 74         }
 75     }
 76     else
 77     {
 78         T[x].size--;
 79         int p=T[x].key > key;
 80         del(key, T[x].son[!p]);
 81     }
 82 }
 83 
 84 int find(int p, int &x) //找出第p小的節點的編號
 85 {
 86     if(p == T[T[x].son[0]].size+1)
 87         return x;
 88     if(p > T[T[x].son[0]].size+1)
 89         find(p-T[T[x].son[0]].size-1, T[x].son[1]);
 90     else
 91         find(p, T[x].son[0]);
 92 }
 93 
 94 int find_NoLarger(int key, int &x) //找出值小於等於key的節點個數
 95 {
 96     if(x == 0)
 97         return 0;
 98     if(T[x].key <= key)
 99         return T[T[x].son[0]].size+1+find_NoLarger(key, T[x].son[1]);
100     else
101         return find_NoLarger(key, T[x].son[0]);    
102 }

View Code

相關題目:POJ 3481 1442 2352

Splay Tree(伸展樹)

核心就是 過程Splay(x, y),即將x節點轉移到y節點的子節點上面(其中y是x的祖先)。

利用其中雙旋的優勢能夠保證查詢復雜度均攤為O(lgn)

一開始理解有些困難,其實實際上不做深入的理解就是,雙旋的過程就是一個建立相對平衡的二叉樹的一個過程。

》對於二叉樹,最極端的情況就是線性插入,使得整棵二叉樹退化為一條鏈。比如你查詢鏈的最后一個節點,之后再次查詢第一個節點。

1)若只是單旋通過Splay(x, 0)將最后一個節點移動到根節點,需要O(n)復雜度,而查詢第一個節點時又需要O(n)復雜度,來來往往就退化成一條鏈了。

2)若是雙旋Splay(x, 0)將最后一個節點移動到根節點上時,移動過程中建立起了相對平衡的二叉樹,需要O(n),也就是查詢第一個節點時,大概是需要O(lgn)復雜度。這就降低了復雜度。可以證明,總的每個操作的均攤復雜度是O(lgn)。

具體證明可以參見 楊思雨《伸展樹的基本操作與應用》

I 用於維護單調隊列 :(以key為維護對象保證單調)

常用版:(支持相同值)

 1  Struct Tree{
 2 
 3 int key, size, fa, son[2];
 4 
 5 }
 6 
 7 void PushUp(int x);
 8 
 9 void Rotate(int x, int p); //0左旋 1右旋
10 
11 void Splay(int x, int To) //將x節點插入到To的子節點中
12 
13 int find(int key) //返回值為key的節點 若無返回0 若有將其轉移到根處
14 
15 int prev() //返回比根值小的最大值 若無返回0 若有將其轉移到根處
16 
17 int succ() //返回比根值大的最小值 若無返回0 若有將其轉移到根處
18 
19 void Insert(int key) //插入key 並且將該節點轉移到根處
20 
21 void Delete(int key) //刪除值為key的節點 若有重點只刪其中一個 x的前驅移動到根處
22 
23 int GetPth(int p) //獲得第p小的節點 並將其轉移到根處
24 
25 int GetRank(int key) //獲得值<=key的節點個數 並將其轉移到根處 若<key只需將<=換為<

模板:

  1 int cnt=1, rt=0;
  2 
  3 struct Tree
  4 {
  5     int key, size, fa, son[2];
  6     void set(int _key, int _size, int _fa)
  7     {
  8         key=_key;
  9         size=_size;
 10         fa=_fa;
 11         son[0]=son[1]=0;
 12     }
 13 }T[MAXN];
 14 
 15 inline void PushUp(int x)
 16 {
 17     T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+1;
 18 }
 19 
 20 inline void Rotate(int x, int p) //0左旋 1右旋
 21 {
 22     int y=T[x].fa;
 23     T[y].son[!p]=T[x].son[p];
 24     T[T[x].son[p]].fa=y;
 25     T[x].fa=T[y].fa;
 26     if(T[x].fa)
 27         T[T[x].fa].son[T[T[x].fa].son[1] == y]=x;
 28     T[x].son[p]=y;
 29     T[y].fa=x;
 30     PushUp(y);
 31     PushUp(x);
 32 }
 33 
 34 void Splay(int x, int To) //將x節點插入到To的子節點中
 35 {
 36     while(T[x].fa != To)
 37     {
 38         if(T[T[x].fa].fa == To)
 39             Rotate(x, T[T[x].fa].son[0] == x);
 40         else
 41         {
 42             int y=T[x].fa, z=T[y].fa;
 43             int p=(T[z].son[0] == y);
 44             if(T[y].son[p] == x)
 45                 Rotate(x, !p), Rotate(x, p); //之字旋
 46             else
 47                 Rotate(y, p), Rotate(x, p); //一字旋
 48         }
 49     }
 50     if(To == 0) rt=x;
 51 }
 52 
 53 int find(int key) //返回值為key的節點 若無返回0 若有將其轉移到根處
 54 {
 55     int x=rt;
 56     while(x && T[x].key != key)
 57         x=T[x].son[key > T[x].key];
 58     if(x) Splay(x, 0);
 59     return x;
 60 }
 61 
 62 int prev() //返回比根值小的最大值 若無返回0 若有將其轉移到根處
 63 {
 64     int x=T[rt].son[0];
 65     if(!x) return 0;
 66     while(T[x].son[1])
 67         x=T[x].son[1];
 68     Splay(x, 0);
 69     return x;
 70 }
 71 
 72 int succ() //返回比根值大的最小值 若無返回0 若有將其轉移到根處
 73 {
 74     int x=T[rt].son[1];
 75     if(!x) return 0;
 76     while(T[x].son[0])
 77         x=T[x].son[0];
 78     Splay(x, 0);
 79     return x;
 80 }
 81 
 82 void Insert(int key) //插入key 並且將該節點轉移到根處
 83 {
 84     if(!rt)
 85         T[rt = cnt++].set(key, 1, 0);
 86     else
 87     {
 88         int x=rt, y=0;
 89         while(x)
 90         {
 91             y=x;
 92             x=T[x].son[key > T[x].key];
 93         }
 94         T[x = cnt++].set(key, 1, y);
 95         T[y].son[key > T[y].key]=x;
 96         Splay(x, 0);
 97     }
 98 }
 99 
100 void Delete(int key) //刪除值為key的節點 若有重點只刪其中一個 x的前驅移動到根處
101 {
102     int x=find(key);
103     if(!x) return;
104     int y=T[x].son[0];
105     while(T[y].son[1])
106         y=T[y].son[1];
107     int z=T[x].son[1];
108     while(T[z].son[0])
109         z=T[z].son[0];
110     if(!y && !z)
111     {
112         rt=0;
113         return;
114     }
115     if(!y)
116     {
117         Splay(z, 0);
118         T[z].son[0]=0;
119         PushUp(z);
120         return;
121     }
122     if(!z)
123     {
124         Splay(y, 0);
125         T[y].son[1]=0;
126         PushUp(y);
127         return;
128     }
129     Splay(y, 0);
130     Splay(z, y);
131     T[z].son[0]=0;
132     PushUp(z);
133     PushUp(y);
134 }
135 
136 int GetPth(int p) //獲得第p小的節點 並將其轉移到根處
137 {
138     if(!rt) return 0;
139     int x=rt, ret=0;
140     while(x)
141     {
142         if(p == T[T[x].son[0]].size+1)
143             break;
144         if(p>T[T[x].son[0]].size+1)
145         {
146             p-=T[T[x].son[0]].size+1;
147             x=T[x].son[1];
148         }
149         else
150             x=T[x].son[0];
151     }
152     Splay(x, 0);
153     return x;
154 }
155 
156 int GetRank(int key) //獲得值<=key的節點個數 並將其轉移到根處 若<key只需將<=換為<
157 {
158     if(!rt) return 0;
159     int x=rt, ret=0, y;
160     while(x)
161     {
162         y=x;
163         if(T[x].key <= key)
164         {
165             ret+=T[T[x].son[0]].size+1;
166             x=T[x].son[1];
167         }
168         else
169             x=T[x].son[0];
170     }
171     Splay(y, 0);
172     return ret;
173 }

View Code

完全版:(支持相同值,支持區間刪除,支持懶惰標記)

 1  Struct Tree{
 2 
 3 int key, num, size, fa, son[2];
 4 
 5 }
 6 
 7 void PushUp(int x);
 8 
 9 void PushDown(int x);
10 
11 int Newnode(int key, int fa); //新建一個節點並返回
12 
13 void Rotate(int x, int p); //0左旋 1右旋
14 
15 void Splay(int x, int To); //將x節點移動到To的子節點中
16 
17 int GetPth(int p, int To); //返回第p小的節點 並移動到To的子節點中
18 
19 int Find(int key); //返回值為key的節點 若無返回0 若有將其轉移到根處
20 
21 int Prev(); //返回根節點的前驅
22 
23 int Succ(); //返回根結點的后繼
24 
25 void Insert(int key); //插入key值
26 
27 void Delete(int key); //刪除值為key的節點
28 
29 int GetRank(int key); //獲得值<=key的節點個數
30 
31 void Delete(int l, int r); //刪除值在[l, r]中的節點 

模板:

  1 int cnt, rt;
  2 int Add[MAXN];
  3 
  4 struct Tree{
  5     int key, num, size, fa, son[2];
  6 }T[MAXN];
  7 
  8 inline void PushUp(int x)
  9 {
 10     T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+T[x].num;
 11 }
 12 
 13 inline void PushDown(int x)
 14 {
 15     if(Add[x])
 16     {
 17         if(T[x].son[0])
 18         {
 19             T[T[x].son[0]].key+=Add[x];
 20             Add[T[x].son[0]]+=Add[x];
 21         }
 22         if(T[x].son[1])
 23         {
 24             T[T[x].son[1]].key+=Add[x];
 25             Add[T[x].son[1]]+=Add[x];
 26         }
 27         Add[x]=0;
 28     }
 29 }
 30 
 31 inline int Newnode(int key, int fa) //新建一個節點並返回
 32 {
 33     ++cnt;
 34     T[cnt].key=key;
 35     T[cnt].num=T[cnt].size=1;
 36     T[cnt].fa=fa;
 37     T[cnt].son[0]=T[cnt].son[1]=0;
 38     return cnt;
 39 }
 40 
 41 inline void Rotate(int x, int p) //0左旋 1右旋
 42 {
 43     int y=T[x].fa;
 44     PushDown(y);
 45     PushDown(x);
 46     T[y].son[!p]=T[x].son[p];
 47     T[T[x].son[p]].fa=y;
 48     T[x].fa=T[y].fa;
 49     if(T[x].fa)
 50         T[T[x].fa].son[T[T[x].fa].son[1] == y]=x;
 51     T[x].son[p]=y;
 52     T[y].fa=x;
 53     PushUp(y);
 54     PushUp(x);
 55 }
 56 
 57 void Splay(int x, int To) //將x節點移動到To的子節點中
 58 {
 59     while(T[x].fa != To)
 60     {
 61         if(T[T[x].fa].fa == To)
 62             Rotate(x, T[T[x].fa].son[0] == x);
 63         else
 64         {
 65             int y=T[x].fa, z=T[y].fa;
 66             int p=(T[z].son[0] == y);
 67             if(T[y].son[p] == x)
 68                 Rotate(x, !p), Rotate(x, p); //之字旋
 69             else
 70                 Rotate(y, p), Rotate(x, p); //一字旋
 71         }
 72     }
 73     if(To == 0) rt=x;
 74 }
 75 
 76 int GetPth(int p, int To) //返回第p小的節點 並移動到To的子節點中
 77 {
 78     if(!rt || p > T[rt].size) return 0;
 79     int x=rt;
 80     while(x)
 81     {
 82         PushDown(x);
 83         if(p >= T[T[x].son[0]].size+1 && p <= T[T[x].son[0]].size+T[x].num)
 84             break;
 85         if(p > T[T[x].son[0]].size+T[x].num)
 86         {
 87             p-=T[T[x].son[0]].size+T[x].num;
 88             x=T[x].son[1];
 89         }
 90         else
 91             x=T[x].son[0];
 92     }
 93     Splay(x, 0);
 94     return x;
 95 }
 96 
 97 int Find(int key) //返回值為key的節點 若無返回0 若有將其轉移到根處
 98 {
 99     if(!rt) return 0;
100     int x=rt;
101     while(x)
102     {
103         PushDown(x);
104         if(T[x].key == key) break;
105         x=T[x].son[key > T[x].key];
106     }
107     if(x) Splay(x, 0);
108     return x;
109 }
110 
111 int Prev() //返回根節點的前驅 非重點
112 {
113     if(!rt || !T[rt].son[0]) return 0;
114     int x=T[rt].son[0];
115     while(T[x].son[1])
116     {
117         PushDown(x);
118         x=T[x].son[1];
119     }
120     Splay(x, 0);
121     return x;
122 }
123 
124 int Succ() //返回根結點的后繼 非重點
125 {
126     if(!rt || !T[rt].son[1]) return 0;
127     int x=T[rt].son[1];
128     while(T[x].son[0])
129     {
130         PushDown(x);
131         x=T[x].son[0];
132     }
133     Splay(x, 0);
134     return x;
135 }
136 
137 void Insert(int key) //插入key值
138 {
139     if(!rt)
140         rt=Newnode(key, 0);
141     else
142     {
143         int x=rt, y=0;
144         while(x)
145         {
146             PushDown(x);
147             y=x;
148             if(T[x].key == key)
149             {
150                 T[x].num++;
151                 T[x].size++;
152                 break;
153             }
154             T[x].size++;
155             x=T[x].son[key > T[x].key];
156         }
157         if(!x)
158             x=T[y].son[key > T[y].key]=Newnode(key, y);
159         Splay(x, 0);
160     }
161 }
162 
163 void Delete(int key) //刪除值為key的節點1個
164 {
165     int x=Find(key);
166     if(!x) return;
167     if(T[x].num>1)
168     {
169         T[x].num--;
170         PushUp(x);
171         return;
172     }
173     int y=T[x].son[0];
174     while(T[y].son[1])
175         y=T[y].son[1];
176     int z=T[x].son[1];
177     while(T[z].son[0])
178         z=T[z].son[0];
179     if(!y && !z)
180     {
181         rt=0;
182         return;
183     }
184     if(!y)
185     {
186         Splay(z, 0);
187         T[z].son[0]=0;
188         PushUp(z);
189         return;
190     }
191     if(!z)
192     {
193         Splay(y, 0);
194         T[y].son[1]=0;
195         PushUp(y);
196         return;
197     }
198     Splay(y, 0);
199     Splay(z, y);
200     T[z].son[0]=0;
201     PushUp(z);
202     PushUp(y);
203 }
204 
205 int GetRank(int key) //獲得值<=key的節點個數
206 {
207     if(!Find(key))
208     {
209         Insert(key);
210         int tmp=T[T[rt].son[0]].size;
211         Delete(key);
212         return tmp;
213     }
214     else
215         return T[T[rt].son[0]].size+T[rt].num;
216 }
217 
218 void Delete(int l, int r) //刪除值在[l, r]中的所有節點 l!=r
219 {
220     if(!Find(l)) Insert(l);
221     int p=Prev();
222     if(!Find(r)) Insert(r);
223     int q=Succ();
224     if(!p && !q)
225     {
226         rt=0;
227         return;
228     }
229     if(!p)
230     {
231         T[rt].son[0]=0;
232         PushUp(rt);
233         return;
234     }
235     if(!q)
236     {
237         Splay(p, 0);
238         T[rt].son[1]=0;
239         PushUp(rt);
240         return;
241     }
242     Splay(p, q);
243     T[p].son[1]=0;
244     PushUp(p);
245     PushUp(q);
246 }

View Code

相關題目:HNOI 2002 POJ3481 POJ2352 POJ1442

NOI2004 郁悶的出納員

II 用於維護序列: (以序列下標為對象維護,相當於對區間操作)(能夠完成線段樹的操作及其不能完成的操作)

 1  Struct Tree{
 2 
 3 int key, sum, size, fa, son[2];
 4 
 5 }
 6 
 7 支持操作:
 8 
 9 void PushUp(int x);
10 
11 void PushDown(int x);
12 
13 int MakeTree(int l, int r, int a[]); //新建一個子樹返回根節點
14 
15 void Rotate(int x, int p); //0左旋 1右旋
16 
17 void Splay(int x, int To); //將x節點移動到To的子節點中
18 
19 int Select(int p, int To); //將第p個數移動到To的子節點中 並返回該節點
20 
21 int Find(int key); //返回值為key的節點 若無返回0 若有將其轉移到根處
22 
23 int Prev(); //返回根節點的前驅
24 
25 int Succ(); //返回根結點的后繼
26 
27 void Insert(int p, int l, int r, int a[]) //將a[l .. r]的數插入到下標為p后面
28 
29 void Delete(int l, int r); //刪除區間[l, r]中的節點
30 
31 int Query(int l, int r); //返回[l, r]的和
32 
33 待補充。。
34 
35 Size Balance Tree
36 
37 和上述兩種二叉樹比起來,SBT可能是最像真正平衡二叉樹吧。
38 
39 SBT能夠保證樹的高度在lgn,這樣對於插入,刪除操作都能夠准確保證時間復雜度在O(lgn)
40 
41 Maintain操作事實上理解起來也是挺簡單的,至於證明參見CQF神牛的 《SBT》 

模版:

  1 int cnt, rt;
  2 
  3 struct Tree
  4 {
  5     int key, size, son[2];
  6 }T[MAXN];
  7 
  8 inline void PushUp(int x)
  9 {
 10     T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+1;
 11 }
 12 
 13 inline int Newnode(int key)
 14 {
 15     ++cnt;
 16     T[cnt].key=key;
 17     T[cnt].size=1;
 18     T[cnt].son[0]=T[cnt].son[1]=0;
 19     return cnt;
 20 }
 21 
 22 void Rotate(int p, int &x)
 23 {
 24     int y=T[x].son[!p];
 25     T[x].son[!p]=T[y].son[p];
 26     T[y].son[p]=x;
 27     PushUp(x);
 28     PushUp(y);
 29     x=y;
 30 }
 31 
 32 void Maintain(int &x, int p) //維護SBT的!p子樹
 33 {
 34     if(T[T[T[x].son[p]].son[p]].size > T[T[x].son[!p]].size)
 35         Rotate(!p, x);
 36     else if(T[T[T[x].son[p]].son[!p]].size > T[T[x].son[!p]].size)
 37         Rotate(p, T[x].son[p]), Rotate(!p, x);
 38     else return;
 39     Maintain(T[x].son[0], 0);
 40     Maintain(T[x].son[1], 1);
 41     Maintain(x, 0);
 42     Maintain(x, 1);
 43 }
 44 
 45 inline int Prev() //返回比根值小的最大值 若無返回0
 46 {
 47     int x=T[rt].son[0];
 48     if(!x) return 0;
 49     while(T[x].son[1])
 50         x=T[x].son[1];
 51     return x;
 52 }
 53 
 54 inline int Succ() //返回比根值大的最小值 若無返回0
 55 {
 56     int x=T[rt].son[1];
 57     if(!x) return 0;
 58     while(T[x].son[0])
 59         x=T[x].son[0];
 60     return x;
 61 }
 62 
 63 void Insert(int key, int &x)
 64 {
 65     if(!x) x=Newnode(key);
 66     else
 67     {
 68         T[x].size++;
 69         Insert(key, T[x].son[key > T[x].key]);
 70         Maintain(x, key > T[x].key);
 71     }
 72 }
 73 
 74 bool Delete(int key, int &x) //刪除值為key的節點 key可以不存在
 75 {
 76     if(!x) return 0;
 77     if(T[x].key == key)
 78     {
 79         if(!T[x].son[0])
 80         {
 81             x=T[x].son[1];
 82             return 1;
 83         }
 84         if(!T[x].son[1])
 85         {
 86             x=T[x].son[0];
 87             return 1;
 88         }
 89         int y=Prev();
 90         T[x].size--;
 91         return Delete(T[x].key, T[x].son[0]);
 92     }
 93     else
 94         if(Delete(key, T[x].son[key > T[x].key]))
 95         {
 96             T[x].size--;
 97             return 1;
 98         }
 99 }
100 
101 int GetPth(int p, int &x) //返回第p小的節點
102 {
103     if(!x) return 0;
104     if(p == T[T[x].son[0]].size+1)
105         return x;
106     if(p > T[T[x].son[0]].size+1)
107         return GetPth(p-T[T[x].son[0]].size-1, T[x].son[1]);
108     else
109         return GetPth(p, T[x].son[0]);
110 }
111 
112 int GetRank(int key, int &x) //找出值<=key的節點個數
113 {
114     if(!x) return 0;
115     if(T[x].key <= key)
116         return T[T[x].son[0]].size+1+GetRank(key, T[x].son[1]);
117     else
118         return GetRank(key, T[x].son[0]);
119 }

View Code

相關題目:POJ 3481

上述題均為用於測試平衡樹基本操作的題目。

提高題:

[NOI2005]維修數列

[POJ3580]SuperMemo

[HNOI2004]寵物收養所

在文章的最后貼上一個二叉樹專題訓練https://vjudge.net/contest/84416;jsessionid=E73DCD38F70FF141A029A2DB5958B2F1

喜歡的點個贊並訂閱我們,我們將會提供最優質的文章供大家學習參考,歡迎大家一起學習QAQ


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM