一,分析平衡二叉查找樹有什麼意義?
平衡二叉查找樹是對二叉查找樹的改進,那二叉查找樹哪些地方是不盡人意的呢?
在分析二叉查找樹的平均查找長度時,會發現,二叉查找樹的平均查找長度與二叉查找樹
的形態有關係,最壞的情況是退化爲鏈表,查找變爲線性查找,平均查找長度爲(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節點的下一個。
這兩個特殊的點很重要,因爲後面的失衡類型就是根據這兩個點的平衡因子來判斷的。
插入節點很簡單,主要就是插入節點後有可能破壞平衡,那就必須將非平衡的樹調整爲平衡樹。
現在將非平衡的情況分爲四種,每種情況都有自己的調整方法。
1,LL型。(左邊重,需往右邊轉)
LL型的判斷依據就是:A節點平衡因子爲2,B節點的平衡因子爲1.
即: A->bf = 2, B->bf = 1.
如果失衡類型是LL型,這調整算法如下:
以B點爲軸,將A節點做順時針旋轉,然後將B的右子樹作爲A的左子樹。
算法的代碼實現如下:(可結合上面的圖看)
- case LL:
- B = A->lchild;//該類型B節點所在的位置
- A->lchild
= B->rchild;//將B節點的右子樹交給A,作爲A的左子樹。
- B->rchild
= A;//把A作爲B的右子樹。
- A->bf
= B->bf
= 0;//更新A,B節點的平衡因子的值。
- if
(father_A ==
NULL)
*root = B;//如果A是根,則現在把B節點設置爲根節點。
- else if (A == father_A->lchild) father_A->lchild = B;//如果原來A是father_A的
- //左孩子,則現在把B,作爲father_A的左孩子。否則,作爲father_A的右孩子,就是用B的取代A原來的位置。
- else father_A->rchild
= B;
- break;
RR型的判斷依據就是:A節點平衡因子爲-2,B節點的平衡因子爲-1.
即: A->bf = -2, B->bf = -1.
如果失衡類型是RR型,這調整算法如下:
該類型的調整和LL型調整差不多。以B節點爲軸,將A節點作逆時針旋轉,然後,把B的
左子樹給A,作爲A的右子樹。
算法的代碼實現如下:
- case RR:
- B = A->rchild;//該類型B節點所在的位置
- A->rchild
= B->lchild;//把B的左子樹給A,充當A的右子樹。
- B->lchild
= A;//將A充當B的左子樹,完成了逆時針旋轉。
- A->bf
= B->bf
= 0;//重新設置平衡因子。
- if
(father_A ==
NULL)
*root = B;//看原來A在整個樹中的位置,現在將B取代A的位置
- else
if (A == father_A->lchild) father_A->lchild
= B;
- else father_A->rchild
= B;
- break;
3,LR型。(左邊,右邊都重,都需要轉)
該類型又引入了一個特殊節點C。該節點很好判斷,和判斷B節點一樣,插入節點的
“祖先節點”那一線上,A的下一個是B,B的下一個就是C節點。
LR型的判斷依據就是:A節點平衡因子爲2,B節點的平衡因子爲-1.
即: A->bf = 2, B->bf = -1.
如果失衡類型是LR型,這調整算法如下:
算法的實現代碼分析:
- case LR:
- B = A->lchild;//該類型B節點的位置
- C = B->rchild;//該類型C節點的位置
- B->rchild
= C->lchild;//C節點的左子樹交給B,作爲B的右子樹。
- A->lchild
= C->rchild;//C節點的右子樹交給A,作爲A的左子樹。
- C->lchild
= B;//B作爲C的左子樹
- C->rchild
= A;//A作爲A的右子樹
- if
(s->key
< C->key)
{//根據插入節點與C節點的位置來更新平衡因子的值
- A->bf
= -1;B->bf
= 0;C->bf
= 0;
- }
else if
(s->key
> C->key)
{
- A->bf
=0;B->bf
= 1;C->bf
= 0;
- }
else {
- A->bf
= 0;B->bf
= 0;
- }
- if
(father_A ==
NULL)
*root = C;//用C節點來取代A節點的位置。
- else
if (A
== father_A->lchild) father_A->lchild
= C;
- else father_A->rchild
= C;
- break;
4,RL型。(左邊,右邊都重,都需要轉)
這種類型和LR型是對稱的,分析起來思路是一樣的。故不再詳細分析。
--------------------------------------------------------------------------------------
四,平衡樹節點的刪除,平衡樹節點的刪除和插入差不多,都有可能導致