資料結構之 AVL平衡樹 (c++)
一
AVL樹是一種高度平衡的二叉查詢樹,這裡將會簡單的提一下其演算法思想,不會討論複雜度的計算.只是想告訴大家,AVL樹的實現,及其平衡的過程.
二
平衡的演算法思想其實非常簡單, 就是將不平衡的二叉樹, 通過旋轉使其平衡. 下面舉個簡單的列子,大家就會明白了.
相比於基本的二叉查詢樹不同的是,AVL樹中多了一個'平衡因子'的資料, 它是用來記錄當前節點的左子樹和右子樹高度的差值. 平衡因子 < 2 , 當 >=2 我們就要進行旋轉 , 首先看2.13 (a) 圖, 在二叉查詢樹中, 插入 5, 6, 3, 4, 2 ,1 可以看到樹是不平衡的. 且根節點5 的平衡因子 = 2 需要旋轉, 由於左子樹比右子樹高, 所以應當右旋 , 右旋以左子(3) 為父,父(5)為右子,可以得到圖2.12(b). 這時(5)這個節點的左孩子應當指向(3)的右孩子, 即 右孫(4)變左孫, 經過此次右旋後得到圖2.12 (c). 左旋同理, 以 4, 3, 6 , 5, 7 ,8 在紙上模擬一下左旋的操作
但如果我們將 5, 2 ,6 ,1, 3, 4 插入可以得到圖2.13(a),和剛才一樣進行右旋操作的時候會得到圖2.13(b). 這和沒有旋轉沒有區別.因為高度差還是2. 且(2)節點的平衡因子為 -1 , 這時就不能直接旋轉. 但可以轉化為圖2.12(a) 然後在進行右旋, 在圖2.13(a) 中將 (3) 節點進行左旋 就可以啦.然後進行右旋就平衡了. 同理可用 4, 3, 7 , 6, 5, 8 進行反向操作.
旋轉的思想講完了, 下面上插入程式碼. 由於刪除操作 實在原有的二叉查詢樹中刪除後重新平衡, 與插入相類似.,就不放程式碼了. 本文章原始碼在github中可自行下載.https://github.com/ZhangSiQihandsome/Data-Structure.git
// 節點資訊 struct Node{ Key key; int depth; int balance; Node* parent; Node* left; Node* right; Node(Key data){ this->key = data; depth = 1; balance = 0; left = NULL; right = NULL; parent = NULL; } Node(Node* node){ key = node->key; depth = 1; balance = 0; parent = NULL; left = NULL; right = NULL; } };
// public 插入函式
void insert(Key data){
if( root == NULL ){
root = new Node(data);
count++;
return;
}
insert(root, data);
}
// privat 插入函式 void insert( Node* node, Key data ){ if( data < node->key ){ if( node->left != NULL ) insert(node->left, data); else{ // 左節點為空 Node* node1 = new Node(data); node1->parent = node; node->left = node1; count++; } } else if( data > node->key ){ if( node->right != NULL ) insert( node->right , data ); else{ Node* node1 = new Node(data); node1->parent = node; node->right = node1; count++; } } // 將插入後的二叉樹進行平衡調整 Balance(node); }
// 右旋
void right_rotate( Node* node ){
// 一次旋轉涉及到的節點包括 雙親,左子做父,右孫
Node* pParent = node->parent, *pLeftSon = node->left, *pLeftGrandSon = pLeftSon->right;
// 左子做父
pLeftSon->parent = pParent;
if( pParent != NULL ){ // 存在父節點
if( node == pParent->left ) // node 為左子
pParent->left = pLeftSon; // 將 node -> parent -> left 指向 node -> left
else if( node == pParent->right ) // node 為右子
pParent->right = pLeftSon;
}
else
root = pLeftSon;
// 根為右子
pLeftSon->right = node;
node->parent = pLeftSon;
// 右孫 變 左孫
node->left = pLeftGrandSon;
if( pLeftGrandSon != NULL )
pLeftGrandSon->parent = node;
// 重新計算平衡因子
node->depth = calcDepth(node);
node->balance = calcBalance(node);
pLeftSon->depth = calcDepth(pLeftSon);
pLeftSon->balance = calcBalance(pLeftSon);
}
// 左旋
void left_rotate( Node* node ){
// 一次旋轉設計到的節點包括 雙親, 右子做父, 左孫
Node* pParent = node->parent, *pRightSon = node->right,*pRightGrandSon = pRightSon->left;
// 右子做父
pRightSon->parent = pParent;
if( pParent != NULL ){
if( node == pParent->left )
pParent->left = pRightSon;
else if( node == pParent->right )
pParent->right = pRightSon;
}
else
root = pRightSon;
// 根為左子
pRightSon->left = node;
node->parent = pRightSon;
// 左孫 變 右孫
node->right = pRightGrandSon;
if( pRightGrandSon != NULL )
pRightGrandSon->parent = node;
// 重新計算平衡因子
node->depth = calcDepth(node);
node->balance = calcBalance(node);
pRightSon->depth = calcDepth(pRightSon);
pRightSon->balance = calcBalance(pRightSon);
}
void Balance(Node*node){
node->balance = calcBalance(node);
// 左子樹高,應該右旋
if( node->balance >= 2 ){
// 如果左子樹右孫高, 先左旋
if( node->left->balance == -1 )
left_rotate(node->left);
// 右旋
right_rotate(node);
}
if( node->balance <= -2 ){
// 如果右子樹左孫高,先右旋
if(node->right->balance == 1)
right_rotate( node->right );
// 左旋
left_rotate(node);
}
node->balance = calcBalance(node); // 重新計算平衡因子
node->depth = calcDepth(node); // 重新計算當前節點深度
}
// 高度差
int calcBalance(Node* node){ // 以傳入的node節點為根 計算左右兩字數的高度差
int left_depth;
int right_depth;
if( node->left != NULL )
left_depth = node->left->depth;
else left_depth = 0;
if( node->right != NULL )
right_depth = node->right->depth;
else right_depth = 0;
return left_depth - right_depth;
}
// 深度
int calcDepth( Node* node ){ // 計算當前節點為根節點 高度高的子樹深度
int depth = 0;
if( node->left != NULL )
depth = node->left->depth;
if( node->right != NULL && depth < node->right->depth )
depth = node->right->depth;
depth++;
return depth;
}