伸展樹

沒看懂,多看幾遍吧html

1 簡介:
伸展樹,或者叫自適應查找樹,是一種用於保存有序集合的簡單高效的數據結構。伸展樹實質上是一個二叉查找樹。容許查找,插入,刪除,刪除最小,刪除最大,分割,合併等許多操做,這些操做的時間複雜度爲O(logN)。因爲伸展樹能夠適應需求序列,所以他們的性能在實際應用中更優秀。
伸展樹支持全部的二叉樹操做。伸展樹不保證最壞狀況下的時間複雜度爲O(logN)。伸展樹的時間複雜度邊界是均攤的。儘管一個單獨的操做可能很耗時,但對於一個任意的操做序列,時間複雜度能夠保證爲O(logN)。node

2 自調整和均攤分析:
    平衡查找樹的一些限制:
一、平衡查找樹每一個節點都須要保存額外的信息。
二、難於實現,所以插入和刪除操做複雜度高,且是潛在的錯誤點。
三、對於簡單的輸入,性能並無什麼提升。
    平衡查找樹能夠考慮提升性能的地方:
一、平衡查找樹在最差、平均和最壞狀況下的時間複雜度在本質上是相同的。
二、對一個節點的訪問,若是第二次訪問的時間小於第一次訪問,將是很是好的事情。
三、90-10法則。在實際狀況中,90%的訪問發生在10%的數據上。
四、處理好那90%的狀況就很好了。
編程

3 均攤時間邊界:
在一顆二叉樹中訪問一個節點的時間複雜度是這個節點的深度。所以,咱們能夠重構樹的結構,使得被常常訪問的節點朝樹根的方向移動。儘管這會引入額外的操做,可是常常被訪問的節點被移動到了靠近根的位置,所以,對於這部分節點,咱們能夠很快的訪問。根據上面的90-10法則,這樣作能夠提升性能。
爲了達到上面的目的,咱們須要使用一種策略──旋轉到根(rotate-to-root)。具體實現以下:
數據結構

旋轉分爲左旋和右旋,這兩個是對稱的。圖示:less

 

爲了敘述的方便,上圖的右旋叫作X繞Y右旋,左旋叫作Y繞X左旋。性能

下圖展現了將節點3旋轉到根:ui

首先節點3繞2左旋,而後3繞節點4右旋。
注意:所查找的數據必須符合上面的90-10法則,不然性能上不升反降!!this

4 基本的自底向上伸展樹:
    應用伸展(splaying)技術,能夠獲得對數均攤邊界的時間複雜度。
    在旋轉的時候,能夠分爲三種狀況:
spa

一、zig狀況。code

 X是查找路徑上咱們須要旋轉的一個非根節點。
    若是X的父節點是根,那麼咱們用下圖所示的方法旋轉X到根:

 

這和一個普通的單旋轉相同。

二、zig-zag狀況。
在這種狀況中,X有一個父節點P和祖父節點G(P的父節點)。X是右子節點,P是左子節點,或者反過來。這個就是雙旋轉。
先是X繞P左旋轉,再接着X繞G右旋轉。

三、zig-zig狀況。
    這和前一個旋轉不一樣。在這種狀況中,X和P都是左子節點或右子節點。
    先是P繞G右旋轉,接着X繞P右旋轉。

下面是splay的僞代碼:

P(X) : 得到X的父節點,G(X) : 得到X的祖父節點(=P(P(X)))。

Function Buttom-up-splay: Do If X 是 P(X) 的左子結點 Then If G(X) 爲空 Then X 繞 P(X)右旋 Else If P(X)是G(X)的左子結點 P(X) 繞G(X)右旋 X 繞P(X)右旋 Else X繞P(X)右旋 X繞P(X)左旋 (P(X)和上面一句的不一樣,是原來的G(X)) Endif Else If X 是 P(X) 的右子結點 Then If G(X) 爲空 Then X 繞 P(X)左旋 Else If P(X)是G(X)的右子結點 P(X) 繞G(X)左旋 X 繞P(X)左旋 Else X繞P(X)左旋 X繞P(X)右旋 (P(X)和上面一句的不一樣,是原來的G(X)) Endif Endif While (P(X) != NULL) EndFunction

仔細分析zig-zag,能夠發現,其實zig-zag就是兩次zig。所以上面的代碼能夠簡化:

Function Buttom-up-splay: Do If X 是 P(X) 的左子結點 Then If P(X)是G(X)的左子結點 P(X) 繞G(X)右旋 Endif X 繞P(X)右旋 Else If X 是 P(X) 的右子結點 Then If P(X)是G(X)的右子結點 P(X) 繞G(X)左旋 Endif X 繞P(X)左旋 Endif While (P(X) != NULL) EndFunction

下面是一個例子,旋轉節點c到根上。 

 

5 基本伸展樹操做:

一、插入:
    當一個節點插入時,伸展操做將執行。所以,新插入的節點在根上。
二、查找:
    若是查找成功(找到),那麼因爲伸展操做,被查找的節點成爲樹的新根。
若是查找失敗(沒有),那麼在查找遇到NULL以前的那個節點成爲新的根。也就是,若是查找的節點在樹中,那麼,此時根上的節點就是距離這個節點最近的節點。
三、查找最大最小:
   查找以後執行伸展。
四、刪除最大最小:
a)刪除最小:
     首先執行查找最小的操做。
  這時,要刪除的節點就在根上。根據二叉查找樹的特色,根沒有左子節點。
  使用根的右子結點做爲新的根,刪除舊的包含最小值的根。
b)刪除最大:
  首先執行查找最大的操做。
  刪除根,並把被刪除的根的左子結點做爲新的根。
五、刪除:
      將要刪除的節點移至根。
      刪除根,剩下兩個子樹L(左子樹)和R(右子樹)。
      使用DeleteMax查找L的最大節點,此時,L的根沒有右子樹。
      使R成爲L的根的右子樹。

以下圖示:

6 自頂向下的伸展樹:
    在自底向上的伸展樹中,咱們須要求一個節點的父節點和祖父節點,所以這種伸展樹難以實現。所以,咱們能夠構建自頂向下的伸展樹。
    當咱們沿着樹向下搜索某個節點X的時候,咱們將搜索路徑上的節點及其子樹移走。咱們構建兩棵臨時的樹──左樹和右樹。沒有被移走的節點構成的樹稱做中樹。在伸展操做的過程當中:
一、當前節點X是中樹的根。
二、左樹L保存小於X的節點。
三、右樹R保存大於X的節點。
開始時候,X是樹T的根,左右樹L和R都是空的。和前面的自下而上相同,自上而下也分三種狀況:

一、zig:

 

如上圖,在搜索到X的時候,所查找的節點比X小,將Y旋轉到中樹的樹根。旋轉以後,X及其右子樹被移動到右樹上。很顯然,右樹上的節點都大於所要查找的節點。注意X被放置在右樹的最小的位置,也就是X及其子樹比原先的右樹中全部的節點都要小。這是因爲越是在路徑前面被移動到右樹的節點,其值越大。讀者能夠分析一下樹的結構,緣由很簡單。

二、zig-zig:

 

在這種狀況下,所查找的節點在Z的子樹中,也就是,所查找的節點比X和Y都小。因此要將X,Y及其右子樹都移動到右樹中。首先是Y繞X右旋,而後Z繞Y右旋,最後將Z的右子樹(此時Z的右子節點爲Y)移動到右樹中。注意右樹中掛載點的位置。

三、zig-zag:

 在這種狀況中,首先將Y右旋到根。這和Zig的狀況是同樣的。而後變成上圖右邊所示的形狀。接着,對Z進行左旋,將Y及其左子樹移動到左樹上。這樣,這種狀況就被分紅了兩個Zig狀況。這樣,在編程的時候就會簡化,可是操做的數目增長(至關於兩次Zig狀況)。
    最後,在查找到節點後,將三棵樹合併。如圖:

將中樹的左右子樹分別鏈接到左樹的右子樹和右樹的左子樹上。將左右樹做爲X的左右子樹。從新最成了一所查找的節點爲根的樹。
    下面給出僞代碼:
    右鏈接:將當前根及其右子樹鏈接到右樹上。左子結點做爲新根。
    左鏈接:將當前根及其左子樹鏈接到左樹上。右子結點做爲新根。
    T : 當前的根節點。

 1 Function Top-Down-Splay  2  Do  3  If X 小於 T Then  4  If X 等於 T 的左子結點 Then  5  右鏈接  6  ElseIf X 小於 T 的左子結點 Then  7  T的左子節點繞T右旋  8  右鏈接  9  Else X大於 T 的左子結點 Then 10  右鏈接 11  左鏈接 12  EndIf 13  ElseIf X大於 T Then 14  IF X 等於 T 的右子結點 Then 15  左鏈接 16  ElseIf X 大於 T 的右子結點 Then 17  T的右子節點繞T左旋 18  左鏈接 19  Else X小於 T 的右子結點‘ Then 20  左鏈接 21  右鏈接 22  EndIf 23  EndIf 24  While !(找到 X或遇到空節點) 25  組合左中右樹 26 EndFunction

一樣,上面的三種狀況也能夠簡化:

 1   Function Top-Down-Splay  2  Do  3  If X 小於 T Then  4  If X 小於 T 的左孩子 Then  5  T的左子節點繞T右旋  6  EndIf  7  右鏈接  8  Else If X大於 T Then  9  If X 大於 T 的右孩子 Then 10  T的右子節點繞T左旋 11  EndIf 12 左鏈接 13  EndIf 14 While  !(找到 X或遇到空節點) 15 組合左中右樹 16     EndFuntion

下面是一個查找節點19的例子:
    在例子中,樹中並無節點19,最後,距離節點最近的節點18被旋轉到了根做爲新的根。節點20也是距離節點19最近的節點,可是節點20沒有成爲新根,這和節點20在原來樹中的位置有關係。

 這個例子是查找節點c:

最後,給一個用C語言實現的例子:

 1 /*
 2  An implementation of top-down splaying  3  D. Sleator <sleator@cs.cmu.edu>  4  March 1992  5   */
 6  #include <stdlib.h>
 7  #include <stdio.h>
 8   int size;  /* number of nodes in the tree */
 9             /* Not actually needed for any of the operations */
 10  typedef struct tree_node Tree;  11   struct tree_node  12  {  13      Tree * left, * right;  14      int item;  15  };  16  
 17  Tree * splay (int i, Tree * t)  18  {  19   /* Simple top down splay, not requiring i to be in the tree t. */
 20   /* What it does is described above. */
 21      Tree N, *l, *r, *y;  22      if (t == NULL)  23          return t;  24      N.left = N.right = NULL;  25      l = r = &N;  26      for (;;)  27  {  28          if (i < t->item)  29  {  30              if (t->left == NULL)  31  {  32                  break;  33  }  34              if (i < t->left->item)  35  {  36                  y = t->left;                           /* rotate right */
 37                  t->left = y->right;  38                  y->right = t;  39                  t = y;  40                  if (t->left == NULL)  41  {  42                      break;  43  }  44  }  45              r->left = t;                               /* link right */
 46              r = t;  47              t = t->left;  48  }  49          else if (i > t->item)  50  {  51              if (t->right == NULL)  52  {  53                  break;  54  }  55              if (i > t->right->item)  56  {  57                  y = t->right;                          /* rotate left */
 58                  t->right = y->left;  59                  y->left = t;  60                  t = y;  61                  if (t->right == NULL)  62  {  63                      break;  64  }  65  }  66              l->right = t;                              /* link left */
 67              l = t;  68              t = t->right;  69  }  70          else    
 71  {  72              break;  73  }  74  }  75      l->right = t->left;                                /* assemble */
 76      r->left = t->right;  77      t->left = N.right;  78      t->right = N.left;  79      return t;  80  }  81   /* Here is how sedgewick would have written this. */
 82  /* It does the same thing. */
 83  Tree * sedgewickized_splay (int i, Tree * t)  84  {  85      Tree N, *l, *r, *y;  86      if (t == NULL)  87  {  88          return t;  89  }  90      N.left = N.right = NULL;  91      l = r = &N;  92      for (;;)  93  {  94          if (i < t->item)  95  {  96              if (t->left != NULL && i < t->left->item)  97  {  98                  y = t->left;  99                  t->left = y->right; 100                  y->right = t; 101                  t = y; 102  } 103              if (t->left == NULL) 104  { 105                  break; 106  } 107              r->left = t; 108              r = t; 109              t = t->left; 110  } 111          else if (i > t->item) 112  { 113              if (t->right != NULL && i > t->right->item) 114  { 115                  y = t->right; 116                  t->right = y->left; 117                  y->left = t; 118                  t = y; 119  } 120              if (t->right == NULL) 121  { 122                  break; 123  } 124              l->right = t; 125              l = t; 126              t = t->right; 127  } 128          else
129  { 130              break; 131  } 132  } 133      l->right=t->left; 134      r->left=t->right; 135      t->left=N.right; 136      t->right=N.left; 137      return t; 138  } 139  
140  Tree * insert(int i, Tree * t) 141  { 142  /* Insert i into the tree t, unless it's already there. */
143  /* Return a pointer to the resulting tree. */
144      Tree * new; 145      
146      new = (Tree *) malloc (sizeof (Tree)); 147      if (new == NULL) 148  { 149          printf("Ran out of space\n"); 150          exit(1); 151  } 152      new->item = i; 153      if (t == NULL) 154  { 155          new->left = new->right = NULL; 156          size = 1; 157          return new; 158  } 159      t = splay(i,t); 160      if (i < t->item) 161  { 162          new->left = t->left; 163          new->right = t; 164          t->left = NULL; 165          size ++; 166          return new; 167  } 168      else if (i > t->item) 169  { 170          new->right = t->right; 171          new->left = t; 172          t->right = NULL; 173          size++; 174          return new; 175  } 176      else
177  { 178          /* We get here if it's already in the tree */
179          /* Don't add it again */
180          free(new); 181          return t; 182  } 183  } 184  
185  Tree * delete(int i, Tree * t) 186  { 187  /* Deletes i from the tree if it's there. */
188  /* Return a pointer to the resulting tree. */
189      Tree * x; 190      if (t==NULL) 191  { 192          return NULL; 193  } 194      t = splay(i,t); 195      if (i == t->item) 196      {               /* found it */
197          if (t->left == NULL) 198  { 199              x = t->right; 200  } 201          else
202  { 203              x = splay(i, t->left); 204              x->right = t->right; 205  } 206          size--; 207  free(t); 208          return x; 209  } 210      return t;                         /* It wasn't there */
211  } 212  
213  int main(int argv, char *argc[]) 214  { 215  /* A sample use of these functions. Start with the empty tree, */
216  /* insert some stuff into it, and then delete it */
217      Tree * root; 218      int i; 219      root = NULL;              /* the empty tree */
220      size = 0; 221      for (i = 0; i < 1024; i++) 222  { 223          root = insert((541*i) & (1023), root); 224  } 225      printf("size = %d\n", size); 226      for (i = 0; i < 1024; i++) 227  { 228          root = delete((541*i) & (1023), root); 229  } 230      printf("size = %d\n", size); 231  }

原文摘自:

http://www.cnblogs.com/kernel_hcy/archive/2010/03/17/1688360.html

相關文章
相關標籤/搜索