二叉查找樹在極端狀況下,會退化爲鏈表,好比一個排好序的數組,構建成二叉樹後,就是一顆所有左傾或右傾的樹,這時候查找的時間爲O(N),AVL樹是帶有平衡條件的二叉查找樹,它的每一個節點左子樹和右子樹的高度最多相差1,它會保證樹的高度爲O(logN),因此在查找時,能保證最壞狀況下時間爲O(logN),AVL樹節點中須要一個成員存儲高度屬性。node
AVL樹節點能夠定義以下:數組
1 struct AVLTreeNode { 2 AVLTreeNode* pLeft; 3 AVLTreeNode* pRight; 4 int nData; 5 int height; 6 };
計算節點高度代碼以下:spa
1 int AVLTreeHeight(AVLTreeNode* pNode) { 2 if (pNode == nullptr) 3 return 0; 4 else { 5 return Max(AVLTreeHeight(pNode->pLeft), AVLTreeHeight(pNode->pRight)) + 1; 6 } 7 }
要維護樹的平衡,每一個節點左右節點的高度差不能超過1,計算一個節點的高度差代碼以下:3d
1 int AVLTreeNodeFactor(AVLTreeNode* pNode) { 2 //left sub node's height - right sub node's height 3 if (pNode == nullptr) 4 return 0; 5 6 int leftHeight = AVLTreeHeight(pNode->pLeft); 7 int rightHeight = AVLTreeHeight(pNode->pRight); 8 9 return leftHeight - rightHeight; 10 }
在討論如何修復樹的平衡以前,先來討論下旋轉,旋轉是對樹的修正,是AVL樹,紅黑樹維持平衡的基礎操做。
旋轉後依然維持二叉樹性質code
左旋轉代碼以下:blog
1 AVLTreeNode* AVLTreeNodeLeftRotate(AVLTreeNode* pNode) { 2 if (pNode == nullptr) 3 return nullptr; 4 5 AVLTreeNode* pTemp = pNode->pRight; 6 pNode->pRight = pTemp->pLeft; 7 pTemp->pLeft = pNode; 8 9 //recalc pTemp and pNode 's height 10 pTemp->height = AVLTreeHeight(pTemp); 11 pNode->height = AVLTreeHeight(pNode); 12 13 return pTemp; 14 }
右旋轉代碼以下:遞歸
1 AVLTreeNode* AVLTreeNodeRightRotate(AVLTreeNode* pNode) { 2 if (pNode == nullptr) 3 return nullptr; 4 5 AVLTreeNode* pTemp = pNode->pLeft; 6 pNode->pLeft = pTemp->pRight; 7 pTemp->pRight = pNode; 8 9 //recalc pTemp and pNode 's height 10 pTemp->height = AVLTreeHeight(pTemp); 11 pNode->height = AVLTreeHeight(pNode); 12 return pTemp; 13 }
當插入或刪除節點時,有四種樹不平衡的狀況,下面逐一討論。
1、LL和LR
設某節點N在插入或刪除後,其節點高度差爲factor ,若是factor的值大於1,則繼續判斷節點N的左子節點高度差,若是左子節點高度差大於0,咱們稱這種狀況爲LL,若是左子節點高度差小於0,則稱之爲LR。
以上圖所示,在插入節點D後,致使A節點左邊高度爲2,右邊高度爲0,致使失去平衡,對A右旋轉便可恢復平衡class
而對於LR的情形,在插入E節點後,A失去平衡,這時候,先對A的左子節點,也就是B進行左旋轉,轉化爲LL的情形,再對A進行右旋轉。基礎
2、RR和RL二叉樹
這兩種情形只是LL和LR的鏡像問題,取對稱操做 就能夠了。LL和RR這兩種狀況是不平衡發生在外側,而LR和RL這兩種狀況是不平衡發生在樹內側
拿代碼來表示這四種情形以下:AVLTreeReblance的參數pNode爲失去平衡的節點,返回的節點是在旋轉前以pNode爲根節點的子樹在旋轉後新的子樹根節點。就拿LR爲例,傳入的參數是A節點,返回的參數是B節點。
1 AVLTreeNode* AVLTreeReblance(AVLTreeNode* pNode) { 2 if (pNode == nullptr) 3 return nullptr; 4 5 int nFactor = AVLTreeNodeFactor(pNode); 6 if (nFactor > 1) { 7 //LL or LR 8 if (AVLTreeNodeFactor(pNode->pLeft) >= 0)//LL 9 return AVLTreeNodeRightRotate(pNode); 10 else {//LR 11 //first:left rotate on sub left 12 pNode->pLeft = AVLTreeNodeLeftRotate(pNode->pLeft); 13 //second: right rotate on self 14 return AVLTreeNodeRightRotate(pNode); 15 } 16 } 17 else if (nFactor < -1) { 18 //RR or RL 19 if (AVLTreeNodeFactor(pNode->pRight) <= 0)//RR 20 return AVLTreeNodeLeftRotate(pNode); 21 else { 22 //RL 23 //first:right rotate on sub left 24 pNode->pRight = AVLTreeNodeRightRotate(pNode->pRight); 25 //second: right rotate on self 26 return AVLTreeNodeLeftRotate(pNode); 27 } 28 } 29 else 30 return pNode; 31 }
AVL樹的插入
AVL樹在插入後,就須要更新重新插入節點到根節點路徑上那些節點的高度信息,而且對這條路徑上的全部節點進行從新平衡。採用遞歸插入,能夠經過遞歸的回溯來完成。
1 AVLTreeNode* AVLTreeInsert(AVLTreeNode* pRoot, int nData) { 2 if (pRoot == nullptr) { 3 pRoot = new AVLTreeNode; 4 pRoot->pLeft = pRoot->pRight = nullptr; 5 pRoot->nData = nData; 6 pRoot->height = 0; 7 } 8 else { 9 if (nData > pRoot->nData) { 10 pRoot->pRight = AVLTreeInsert(pRoot->pRight, nData); 11 } 12 else if (nData < pRoot->nData) { 13 pRoot->pLeft = AVLTreeInsert(pRoot->pLeft, nData); 14 } 15 } 16 pRoot->height = AVLTreeHeight(pRoot);//更新節點高度 17 pRoot = AVLTreeReblance(pRoot);//對節點從新平衡 18 return pRoot; 19 }
AVL樹的刪除
AVL樹的刪除跟普通二叉查找樹刪除流程同樣,只是在刪除完後,要對新子樹根節點進行平衡操做。一樣的,路徑上的節點高度也要更新,並對被刪除節點到根節點作修復
1 AVLTreeNode* AVLTreeDelete(AVLTreeNode* pRoot, int nData) { 2 if (pRoot == nullptr) 3 return nullptr; 4 5 AVLTreeNode* pResult = nullptr; 6 if (nData > pRoot->nData) { 7 pRoot->pRight = AVLTreeDelete(pRoot->pRight, nData); 8 pResult = pRoot; 9 } 10 else if (nData < pRoot->nData) { 11 pRoot->pLeft = AVLTreeDelete(pRoot->pLeft, nData); 12 pResult = pRoot; 13 } 14 else { 15 if (pRoot->pRight && pRoot->pLeft) { 16 AVLTreeNode* pTemp = AVLTreeMinimumNode(pRoot->pRight); 17 pRoot->nData = pTemp->nData; 18 pRoot->pRight = AVLTreeDelete(pRoot->pRight, pTemp->nData); 19 pResult = pRoot; 20 } 21 else { 22 AVLTreeNode* pTemp = (pRoot->pRight == nullptr)?pRoot->pLeft:pRoot->pRight; 23 delete pRoot; 24 pResult = pTemp; 25 } 26 } 27 if (pResult) 28 pResult->height = AVLTreeHeight(pResult); 29 return AVLTreeReblance(pResult); 30 }