平衡樹的調整 案例與代碼

,分析平衡二叉查找樹有什麼意義?

   平衡二叉查找樹是對二叉查找樹的改進,那二叉查找樹哪些地方是不盡人意的呢?

在分析二叉查找樹的平均查找長度時,會發現,二叉查找樹的平均查找長度與二叉查找樹

的形態有關係,最壞的情況是退化爲鏈表,查找變爲線性查找,平均查找長度爲(n 1)/2.

好的情況就是樹的形態與折半查找的判斷樹形式。平均查找長度爲logN


平衡二叉樹就是爲了保證樹的形態向“樹”的方向走。避免了二叉查找樹退化爲鏈表的可能。

從而提高了查找效率。其實平衡二叉查找樹與二叉查找樹的區別並不是很大,平衡樹在“改變”

樹的時候會維護樹的形態,“改變”無非就兩種,插入節點和刪除節點,而樹的查找只“讀”

了樹,並沒改變,所以樹的查找,平衡樹和查找樹是一樣的。

例如:


現在我要使用24,12,53,28,45,90創建查找樹,如果創建的二叉查找樹(如左圖),

則平均查找長度:(1 2 2 3 4 3)/6 = 15/6 如果創建的是平衡二叉查找樹(如右圖)

則平均查找長度:(2 3 2 3 1 3)/6 = 14/6. 平衡二叉樹的查找效率要高,

這個例子是借用上次使用的,可能代表性不是強。

----------------------------------------------------------------


二,平衡二叉查找樹相關的重要概念。


1,什麼是平衡二叉查找樹?



平衡二叉查找樹又稱爲平衡二叉排序樹,又稱爲AVL樹,是二叉查找樹的改進
     定義(滿足如下三個條件)
           (1),是二叉查找樹。
           (2),左子樹與右子樹的深度之差的絕對值小於或等於1.
           (3),左右子樹也是平衡二叉查找樹。 


2,什麼是平衡因子?

平衡二叉查找樹的每個結點都要描述一個屬性,就是平衡因子,
      它表示結點的左子樹深度與右子樹深度之差。 

該概念的引入也是爲了更好地描述什麼是平衡二叉查找樹。有了這概念後。

我們可以重新對二叉查找樹下個定義。

如果某個二叉查找樹的所有節點的平衡因子只有-1,0,1則說明其實平衡的,

否則說明是不平衡的。
-------------------------------------------------------------------
三,平衡二叉查找樹是如何創建和插入的?如何保證插入一個新節點後樹仍然是

一棵平衡二叉查找樹?


先介紹幾個特殊的節點。

插入一個新的節點,只有該節點的祖先節點的平衡因子會變化,其他節點的

平衡因子都不會變化。


如上圖,插入15這個節點後,平衡因子變化的只有20,25,40。都是15的“祖先節點”。



A節點:爲插入點最底層“祖先節點”最可能的失衡點。比如插入的節點是15,故插入的位置是節點20的左孩子,這從20這個節點開始遍歷祖先節點,取最近的的最可能失衡點,
這兒就是40這個節點。如果沒有找到,說明插入這個節點不可能破壞平衡
B節點就是該祖先節點一條線中A節點的下一個。

這兩個特殊的點很重要,因爲後面的失衡類型就是根據這兩個點的平衡因子來判斷的。









插入節點很簡單,主要就是插入節點後有可能破壞平衡,那就必須將非平衡的樹調整爲平衡樹。


現在將非平衡的情況分爲四種,每種情況都有自己的調整方法。


1LL型。(左邊重,需往右邊轉)


LL型的判斷依據就是:A節點平衡因子爲2B節點的平衡因子爲1.

: A->bf = 2, B->bf = 1.

如果失衡類型是LL型,這調整算法如下:







B點爲軸,將A節點做順時針旋轉,然後將B的右子樹作爲A的左子樹。

算法的代碼實現如下:(可結合上面的圖看)


  1. case LL:
  2.             B = A->lchild;//該類型B節點所在的位置
  3.             A->lchild = B->rchild;//將B節點的右子樹交給A,作爲A的左子樹
  4.             B->rchild = A;//把A作爲B的右子樹。
  5.             A->bf = B->bf = 0;//更新A,B節點的平衡因子的值。
  6.             if (father_A == NULL) *root = B;//如果A是根,則現在把B節點設置爲根節點。
  7.             else if (A == father_A->lchild) father_A->lchild = B;//如果原來A是father_A的
  8. //左孩子,則現在把B,作爲father_A的左孩子。否則,作爲father_A的右孩子,就是用B的取代A原來的位置。
  9.             else    father_A->rchild = B;
  10.             break;
該算法的過程實現起來並不難,主要還是有人家的理論在這兒放着。詳細的過程看代碼註釋。

2RR型。與LL型是對稱的。(右邊重,需往左邊轉)

RR型的判斷依據就是:A節點平衡因子爲-2B節點的平衡因子爲-1.

: A->bf = -2, B->bf = -1.

如果失衡類型是RR型,這調整算法如下:


該類型的調整和LL型調整差不多。以B節點爲軸,將A節點作逆時針旋轉,然後,把B

左子樹給A,作爲A的右子樹。

算法的代碼實現如下:


  1. case RR:
  2.     B = A->rchild;//該類型B節點所在的位置
  3.     A->rchild = B->lchild;//把B的左子樹給A,充當A的右子樹。
  4.     B->lchild = A;//將A充當B的左子樹,完成了逆時針旋轉。
  5.     A->bf = B->bf = 0;//重新設置平衡因子。
  6.     if (father_A == NULL) *root = B;//看原來A在整個樹中的位置,現在將B取代A的位置
  7.     else if (A == father_A->lchild) father_A->lchild = B;
  8.     else father_A->rchild = B;
  9.     break;
該類型和LL型是對稱的,只要理解了其中一個,另外一個很好理解。

3LR型。(左邊,右邊都重,都需要轉)


該類型又引入了一個特殊節點C。該節點很好判斷,和判斷B節點一樣,插入節點的

“祖先節點”那一線上,A的下一個是BB的下一個就是C節點。


LR型的判斷依據就是:A節點平衡因子爲2B節點的平衡因子爲-1.

: A->bf = 2, B->bf = -1.

如果失衡類型是LR型,這調整算法如下:

算法的實現代碼分析:


  1. case LR:
  2.             B = A->lchild;//該類型B節點的位置
  3.             C = B->rchild;//該類型C節點的位置
  4.             B->rchild = C->lchild;//C節點的左子樹交給B,作爲B的右子樹。
  5.             A->lchild = C->rchild;//C節點的右子樹交給A,作爲A的左子樹。
  6.             C->lchild = B;//B作爲C的左子樹
  7.             C->rchild = A;//A作爲A的右子樹
  8.             if (s->key < C->key) {//根據插入節點與C節點的位置來更新平衡因子的值
  9.                 A->bf = -1;B->bf = 0;C->bf = 0;
  10.             } else if (s->key > C->key) {
  11.                 A->bf =0;B->bf = 1;C->bf = 0;
  12.             } else {
  13.                 A->bf = 0;B->bf = 0;
  14.             }
  15.             if (father_A == NULL) *root = C;//用C節點來取代A節點的位置。
  16.             else if (A == father_A->lchild) father_A->lchild = C;
  17.             else father_A->rchild = C;
  18.             break;
該類型的調整算法見代碼註釋。

4RL型。(左邊,右邊都重,都需要轉)
      這種類型和LR型是對稱的,分析起來思路是一樣的。故不再詳細分析。

--------------------------------------------------------------------------------------
四,平衡樹節點的刪除,平衡樹節點的刪除和插入差不多,都有可能導致
平衡樹的失衡,在刪除節點後同樣要找到A點和B點,然後判斷是否有失衡,
如果失衡了,然後根據失衡的類型作出調整。調整的算法見上面的分析。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章