平衡二叉樹相信大家都不陌生了,它是一顆平衡的二叉排序樹,它的查詢、插入刪除的時間複雜度均為O(logn),是非常好且重要的動態查詢結構,因此被廣泛的應用。而平衡二叉樹的實現因為引入了旋轉操作而經常讓人難以理解,樓主寫這篇文章的目的,其實是希望能夠通過造輪子的方式,幫助到大家完全理解從普通二叉排序樹逐步優化到平衡二叉樹(AVL樹)的過程。
一、平衡二叉樹之AVL樹簡介
而AVL樹是一種高度平衡的平衡二叉樹,樹中的任意節點的左右子樹的高度之差不超過1。如果將二叉樹上結點的平衡因子BF(Balanced Factor)定義為該結點的左子樹與右子樹的高度之差,根據AVL樹的定義,AVL樹中的任意結點的平衡因子只可能是-1(右子樹高於左子樹)、0或者1(左子樹高於右子樹),在某些圖中也會表示為絕對高度差,即0,1,2這種形式,請注意理解。它通過樹的旋轉操作來維護平衡特性。下面我會逐步通過C++程式碼來理解、最終實現AVL樹。
二、AVL樹實現
1.二叉排序樹實現
AVL樹是一顆平衡的二叉排序樹,所以為了實現AVL樹,我們可以先從實現一顆普通的二叉排序樹入手,實現一顆普通二叉排序樹。為了接下來將二叉排序樹優化為AVL樹,程式碼中將樹直接取名為AVL。
樹中節點具有雙親和左右兩個子節點的指標,
插入刪除操作都是先查詢到節點位置,再進行插入或者刪除。
刪除時,如果刪除的是同時有左右子樹的非葉子節點,則尋找該節點的後繼節點,替換該節點再將後繼節點刪除,這裡不做過多解釋,程式碼如下:
#include<stdio.h> #include<malloc.h> struct AVLNode{ int value; int balance; //AVL樹的平衡欄位,普通二叉排序樹並不需要用到該欄位 AVLNode* left; AVLNode* right; AVLNode* parent; }; //這是一顆普通的二叉排序樹,,命名為AVLTree僅僅因為方便起見 struct AVLTree{ AVLNode* root; }tree; //查詢操作 struct AVLNode* select(int value,AVLNode* root){ if(root==0){ return 0; } if(root->value==value){ return root; } if(root->value>value){ if(root->left) return select(value,root->left); else return root; } if(root->value<value){ if(root->right) return select(value,root->right); else return root; } } //插入操作 void insert(int value,AVLNode* root){ AVLNode* node=select(value,root); if(node==0){ tree.root=(AVLNode*)malloc(sizeof(AVLNode)); tree.root->value=value; tree.root->left=tree.root->right=0; tree.root->parent=0; tree.root->balance=0; } else if(node->value!=value){ if(node->value>value){ node->left=(AVLNode*)malloc(sizeof(AVLNode)); node->left->value=value; node->left->left=node->left->right=0; node->left->parent=node; node->left->balance=0; }else if(node->value<value){ node->right=(AVLNode*)malloc(sizeof(AVLNode)); node->right->value=value; node->right->left=node->right->right=0; node->right->parent=node; node->right->balance=0; } } } void delnodeifhas1childornot(AVLNode* a){ if(a->parent==0){ if(a->left){ tree.root=a->left; a->left->parent=0; }else{ tree.root=a->right; a->right->parent=0; } }else{ if(a->parent->left==a){ if(a->left){ a->parent->left=a->left; a->left->parent=a->parent; }else{ a->parent->left=a->right; if(a->right) a->right->parent=a->parent; } }else{ if(a->left){ a->parent->right=a->left; a->left->parent=a->parent; }else{ a->parent->right=a->right; if(a->right) a->right->parent=a->parent; } } } } struct AVLNode* getmin(AVLNode* a){ if(a->left) getmin(a->left); else return a; } void delnodeifhas2child(AVLNode* a){ AVLNode* after=getmin(a->right); a->value=after->value; delnodeifhas1childornot(after); }
//刪除操作 void del(int value,AVLNode* root){ AVLNode* node=select(value,root); if(node->value==value){ if(node->left&&node->right){ delnodeifhas2child(node); }else{ delnodeifhas1childornot(node); } } }
2.從二叉排序樹到AVL樹
2.1 旋轉操作原理
AVL樹通過左旋右旋來實現它的平衡性。理解左旋和右旋是AVL樹實現的關鍵點,那麼問題來了,所謂左旋右旋又具體是怎麼樣的操作呢?以右旋為例。
初始的AVL樹有兩個節點,a和b,此時樹平衡。
插入節點c,若節點c小於b,則成為b的左子節點,此時a左子樹深度為2,右子樹深度為0,處於不平衡狀態,此時執行右旋操作。
右旋就是使a、b進行右旋1/4圓,使得b節點作為父節點,a節點變為b節點的右節點,b的左子樹在經過右旋操作後仍是b的左子樹。
如果b有右子樹。那麼右旋後b的右子樹將成為a的左子樹。
這就是右旋操作的具體實現,左旋是右旋對稱的過程。當二叉樹插入或者刪除一個節點時,如果破壞了樹的平衡性,就會根據樹的失衡情況進行旋轉操作,如果左子樹高於右子樹,則右旋,如果右子樹高於左子樹,則左旋。
右旋及左旋的程式碼如下:
struct AVLNode* turnRight(AVLNode* a){ // 右旋使a的父節點的子節點替換為b AVLNode* b=a->left; if(a->parent!=0){ if(a->parent->right==a){ a->parent->right=b; }else{ a->parent->left=b; } } b->parent=a->parent; //將b的右子樹作為a的左子樹,並將a作為b的右子樹 a->parent=b; a->left=b->right; if(a->left!=0) a->left->parent=a; b->right=a; //重新設定a、b節點的balance值 setBalance(a); setBalance(b); //返回b節點 return b; }
struct AVLNode* turnLeft(AVLNode* a){ //左旋把a的父節點的子節點替換為b AVLNode* b=a->right; if(a->parent!=0){ if(a->parent->right==a){ a->parent->right=b; }else{ a->parent->left=b; } } b->parent=a->parent; //將a作為b的左子樹,並將b的左子樹作為a的右子樹 a->parent=b; a->right=b->left; b->left=a; if(a->right!=0) a->right->parent=a; //重新設定a\b的balance值 setBalance(a); setBalance(b);
//返回b節點
return b; }
int height(AVLNode* a){ if(a==0){ return 0; } int rightheight=height(a->right); int leftheight=height(a->left); return rightheight > leftheight ? (rightheight+1) : (leftheight+1); } void setBalance(AVLNode* a){ if(a) a->balance=height(a->right)-height(a->left); }
上面將樹從失衡狀態恢復到平衡狀態只進行了一次旋轉操作,事實上還有一種稍微複雜的情況,需要進行兩次旋轉才能將樹重新恢復平衡
如果節點插入到a的左子樹的右節點,則需要先左旋再右旋來重新平衡樹。同理,如果節點插入到a的右子樹的左子節點,則需要先右旋再左旋來重新平衡樹。如果直接進行右旋,生成的樹將仍然是失衡的。
程式碼如下,呼叫了上面實現的旋轉操作:
struct AVLNode* turnLeftThenRight(AVLNode* a){ a->left = turnLeft(a->left); return turnRight(a); } struct AVLNode* turnRightThenLeft(AVLNode* a){ a->right = turnRight(a->right); return turnLeft(a); }
2.2 實現AVL樹插入刪除
AVL樹的insert操作,僅僅在二叉排序樹的insert基礎上增加了rebalance操作。
//AVL樹在insert或者del之後進行rebalance操作,從插入節點的父節點開始,逐層向上遍歷進行rebalance,直到遍歷到根節點,由於樹是平衡的,所以rebalance的時間複雜度是O(logn)。 void rebalance(AVLNode* a){ setBalance(a); if(a->balance== -2){ //如果左子樹比右子樹高2層,需要通過旋轉來重新平衡 if(a->left->balance <=0){ //如果左子節點的左子樹比右子樹高,則進行一次右旋;如果左子節點的右子樹比左子樹高,則先進行左旋,再進行右旋。 a=turnRight(a); }else{ a=turnLeftThenRight(a); } }else if(a->balance==2){ //如果右子樹比左子樹高2層,需要通過旋轉來重新平衡 if(a->right->balance>=0){ //如果右子節點的右子樹比左子樹高,則進行一次左旋;如果右子節點的右子樹比左子樹低,則先進行右旋,再進行左旋。 a=turnLeft(a); }else{ a=turnRightThenLeft(a); } } if(a->parent){ rebalance(a->parent); }else{ tree.root=a; } } void insert(int value,AVLNode* root){ AVLNode* node=select(value,root); if(node==0){ tree.root=(AVLNode*)malloc(sizeof(AVLNode)); tree.root->value=value; tree.root->left=tree.root->right=0; tree.root->parent=0; tree.root->balance=0; } else if(node->value!=value){ if(node->value>value){ node->left=(AVLNode*)malloc(sizeof(AVLNode)); node->left->value=value; node->left->left=node->left->right=0; node->left->parent=node; node->left->balance=0; rebalance(node); //插入完成後進行rebalance }else if(node->value<value){ node->right=(AVLNode*)malloc(sizeof(AVLNode)); node->right->value=value; node->right->left=node->right->right=0; node->right->parent=node; node->right->balance=0; rebalance(node); //插入完成後進行rebalance } } }
AVL樹的刪除操作,也只是在二叉排序樹的刪除操作上,增加了rebalance操作。
void delnodeifhas1childornot(AVLNode* a){ if(a->parent==0){ if(a->left){ tree.root=a->left; a->left->parent=0; }else{ tree.root=a->right; a->right->parent=0; } }else{ if(a->parent->left==a){ if(a->left){ a->parent->left=a->left; a->left->parent=a->parent; }else{ a->parent->left=a->right; if(a->right) a->right->parent=a->parent; } }else{ if(a->left){ a->parent->right=a->left; a->left->parent=a->parent; }else{ a->parent->right=a->right; if(a->right) a->right->parent=a->parent; } } rebalance(a->parent); } } struct AVLNode* getmin(AVLNode* a){ if(a->left) getmin(a->left); else return a; } void delnodeifhas2child(AVLNode* a){ AVLNode* after=getmin(a->right); a->value=after->value; delnodeifhas1childornot(after); } void del(int value,AVLNode* root){ //刪除操作 AVLNode* node=select(value,root); if(node->value==value){ if(node->left&&node->right){ delnodeifhas2child(node); }else{ delnodeifhas1childornot(node); } } }
完整AVL樹c++程式碼如下:
#include<stdio.h> #include<malloc.h> struct AVLNode{ int value; int balance; AVLNode* left; AVLNode* right; AVLNode* parent; }; struct AVLTree{ AVLNode* root; }tree; int height(AVLNode* a){ if(a==0){ return 0; } int rightheight=height(a->right); int leftheight=height(a->left); return rightheight > leftheight ? (rightheight+1) : (leftheight+1); } struct AVLNode* select(int value,AVLNode* root){ if(root==0){ return 0; } if(root->value==value){ return root; } if(root->value>value){ if(root->left) return select(value,root->left); else return root; } if(root->value<value){ if(root->right) return select(value,root->right); else return root; } } void setBalance(AVLNode* a){ if(a) a->balance=height(a->right)-height(a->left); } struct AVLNode* turnLeft(AVLNode* a){ AVLNode* b=a->right; if(a->parent!=0){ if(a->parent->right==a){ a->parent->right=b; }else{ a->parent->left=b; } } b->parent=a->parent; a->parent=b; a->right=b->left; b->left=a; if(a->right!=0) a->right->parent=a; setBalance(a); setBalance(b); return b; } struct AVLNode* turnRight(AVLNode* a){ AVLNode* b=a->left; if(a->parent!=0){ if(a->parent->right==a){ a->parent->right=b; }else{ a->parent->left=b; } } b->parent=a->parent; a->parent=b; a->left=b->right; if(a->left!=0) a->left->parent=a; b->right=a; setBalance(a); setBalance(b); return b; } struct AVLNode* turnLeftThenRight(AVLNode* a){ a->left = turnLeft(a->left); return turnRight(a); } struct AVLNode* turnRightThenLeft(AVLNode* a){ a->right = turnRight(a->right); return turnLeft(a); } void rebalance(AVLNode* a){ setBalance(a); if(a->balance== -2){ if(a->left->balance <=0){ a=turnRight(a); }else{ a=turnLeftThenRight(a); } }else if(a->balance==2){ if(a->right->balance>=0){ a=turnLeft(a); }else{ a=turnRightThenLeft(a); } } if(a->parent){ rebalance(a->parent); }else{ tree.root=a; } } void insert(int value,AVLNode* root){ AVLNode* node=select(value,root); if(node==0){ tree.root=(AVLNode*)malloc(sizeof(AVLNode)); tree.root->value=value; tree.root->left=tree.root->right=0; tree.root->parent=0; tree.root->balance=0; } else if(node->value!=value){ if(node->value>value){ node->left=(AVLNode*)malloc(sizeof(AVLNode)); node->left->value=value; node->left->left=node->left->right=0; node->left->parent=node; node->left->balance=0; rebalance(node); }else if(node->value<value){ node->right=(AVLNode*)malloc(sizeof(AVLNode)); node->right->value=value; node->right->left=node->right->right=0; node->right->parent=node; node->right->balance=0; rebalance(node); } } } void delnodeifhas1childornot(AVLNode* a){ if(a->parent==0){ if(a->left){ tree.root=a->left; a->left->parent=0; }else{ tree.root=a->right; a->right->parent=0; } }else{ if(a->parent->left==a){ if(a->left){ a->parent->left=a->left; a->left->parent=a->parent; }else{ a->parent->left=a->right; if(a->right) a->right->parent=a->parent; } }else{ if(a->left){ a->parent->right=a->left; a->left->parent=a->parent; }else{ a->parent->right=a->right; if(a->right) a->right->parent=a->parent; } } rebalance(a->parent); } } struct AVLNode* getmin(AVLNode* a){ if(a->left) getmin(a->left); else return a; } void delnodeifhas2child(AVLNode* a){ AVLNode* after=getmin(a->right); a->value=after->value; delnodeifhas1childornot(after); } void del(int value,AVLNode* root){ AVLNode* node=select(value,root); if(node->value==value){ if(node->left&&node->right){ delnodeifhas2child(node); }else{ delnodeifhas1childornot(node); } } } void insertNodetest(){ insert(5,tree.root); insert(6,tree.root); insert(7,tree.root); insert(3,tree.root); insert(4,tree.root); insert(4,tree.root); insert(5,tree.root); insert(5,tree.root); insert(3,tree.root); insert(8,tree.root); insert(9,tree.root); insert(10,tree.root); insert(11,tree.root); insert(12,tree.root); } void delNodetest(){ del(6,tree.root); del(11,tree.root); del(12,tree.root); del(3,tree.root); del(3,tree.root); del(5,tree.root); del(9,tree.root); del(12,tree.root); } int main(){ insertNodetest(); // 測試構建AVL樹 delNodetest(); // 測試刪除AVL樹節點 }
至此,我們就完全實現了一顆AVL樹的插入刪除和查詢功能。
(歡迎加qq:1363890602,討論qq群:297572046,備註:程式設計藝術)