[C/C++] AVL樹,插入及刪除

1563 字
8 分鐘
[C/C++] AVL樹,插入及刪除

【輸入】#

可以對AVL樹進行插入或刪除數字

【輸出】#

依前序追蹤輸出AVL樹

【完整程式碼】#

#include
#include
#include
using namespace std;
class Node{
private:
int data;
int height;
Node *left, *right;
public:
Node(int val){ data = val; left = right = NULL; height = 1;}
friend class AVLTree;
};
class AVLTree{
private:
int height(Node* node){
return (!node)? 0: node->height;
}
int getBF(Node* node){
return (!node)? 0: height(node->left) - height(node->right);
}
//旋轉方法
Node* rotate(Node* old_rootT, char direct) {
//重新定義此子樹的rootT節點
Node *new_rootT = (direct=='L')? old_rootT->right: old_rootT->left; //若左轉,新的rootT會是舊rootT->R,若右轉,新的rootT會是舊rootT->L
Node *temp = (direct=='L')? new_rootT->left: new_rootT->right; //若左轉,暫存子樹會是舊rootT->R->L(即新的rootT>L),若右轉,暫存子樹會是舊rootT->L->R(即新的rootT>R)
//旋轉
if(direct=='L'){ //若左轉
new_rootT->left = old_rootT; //舊rooT會變成新的rootT->L
old_rootT->right = temp; //舊rootT->R會是暫存子樹temp(即舊rootT->R->L, 亦即新的rootT->L)
}else{ //若右轉
new_rootT->right = old_rootT; //舊rooT會變成新的rootT->R
old_rootT->left = temp; //舊rootT->L會是暫存子樹temp(即舊rootT->L->R, 亦即新的rootT->R)
}
//更新有改變到的高度(從底至上),所以先update舊rootT
old_rootT->height = max(height(old_rootT->left), height(old_rootT->right)) + 1;
new_rootT->height = max(height(new_rootT->left), height(new_rootT->right)) + 1;
return new_rootT;
}
//插入遞迴式
Node* insertRecursive(Node* rootT, int val){
//使用一般的BST樹插入方法插入數字
if(!rootT) return (new Node(val)); //若目前節點 (或作 目前子樹的根) 為空,即樹葉節點插入新節點至樹葉 (回傳給上一層,此例中上一層也等同rootT,因為是用 rootT = insert(rootT, val)呼叫 )
else if(val data) rootT->left = insertRecursive(rootT->left, val); //若 數字 左子 = insert(rootT->左子) 會 return 一個插入完數字 的子樹 的rootT 給 rootT->左子)
else if(val > rootT->data) rootT->right = insertRecursive(rootT->right, val); //若 數字 > 目前節點,則插入右子樹
else return rootT; //二元樹原則上非key-value數對型態者,不會有重複值
//把rootT 回傳給rootT (即:把自己丟回給自己,不作修改)
rootT->height = max(height(rootT->left), height(rootT->right)) + 1; //插入後,更新目前這個節點的樹高 (取左右子樹高之大者 + 1(自己))
int bf = getBF(rootT); //取得平衡因子
//process if abs(bf)>1
if(bf > 1 && val left->data){ //(平衡因子>1) => 左子樹高,前面必插左邊; (插入數 前面必插左子樹的左邊 {LL}
return rotate(rootT, 'R'); //自己 (目前子樹) 右轉,然後把rootT 回傳給上一層 (同前面提到,此例中上一層也等同rootT,即原本呼叫這個函數的人rootT = insert(rootT, val) )
}
if(bf > 1 && val > rootT->left->data){ //(平衡因子>1) => 左子樹高,前面必插左邊; (插入數 > 左子樹值) => 前面必插左子樹的右邊 {LR}
rootT->left = rotate(rootT->left, 'L'); //自己的左邊 (目前子樹的左子樹) 先左轉
return rotate(rootT, 'R'); //自己 (目前子樹) 再右轉
}
if(bf rootT->right->data){ //(平衡因子 右子樹高,前面必插右邊; (插入數 > 右子樹值) => 前面必插右子樹的右邊 {RR}
return rotate(rootT, 'L'); //自己 (目前子樹) 左轉
}
if(bf right->data){ //(平衡因子 右子樹高,前面必插右邊; (插入數 前面必插右子樹的左邊 {RL}
rootT->right = rotate(rootT->right, 'R'); //自己的右邊 (目前子樹的左子樹) 先右轉
return rotate(rootT, 'L'); //自己 (目前子樹) 再左轉
}
return rootT;
}
//找子樹中最小的樹 (從根開始往左走直到不能走)
Node* minValue(Node* rootT){
Node* ptr = rootT;
while(ptr->left)
ptr = ptr->left;
return ptr;
}
//自AVL樹中刪除
Node* removeRecursive(Node* rootT, int val){
//使用一般的BST樹插入方法刪除數字
if(!rootT) return NULL; //若為空值,那不用刪除,直接回傳給剛剛叫他的人(亦作 return rootT;) (rootT==NULL 即剛剛叫他的人傳入的子樹為空)
else if(val data) rootT->left = removeRecursive(rootT->left, val); //若 數字 rootT->data) rootT->right = removeRecursive(rootT->right, val); //若 數字 > 目前節點,要刪除的數字在目前子樹的右子樹
else{ //上面都不成立,即找到相等的數,進入刪除階段
if(!rootT->left || !rootT->right){ //若目前節點的子樹數量為 0(無子樹) 或 只有單邊子樹
Node* childT = (rootT->left)? rootT->left: rootT->right; //暫存左或右子樹
delete rootT; //釋放目前子樹樹根記憶體
rootT = childT; //令左或右子樹樹根為樹根 (左或右樹直接上拉)
}else{ //若目前節點 左、右子樹皆有
Node* minT = minValue(rootT->right); //取得 右子樹最小的節點
rootT->data = minT->data; //設目前子樹樹根的數字 為剛剛找到的節點的數字
rootT->right = removeRecursive(rootT->right, minT->data); //刪除剛剛找到的節點 (註:因為右子樹最小的節點必無左子樹,故遞迴結果必為第1或第2個if)
}
}
if(!rootT) return rootT; //若目前子樹樹根為空,必平衡且樹高為0,直接回傳
rootT->height = max(height(rootT->left), height(rootT->right)) + 1; //更新目前這個節點的樹高
int bf = getBF(rootT); //取得平衡因子
//process if abs(bf)>1
if(bf > 1 && getBF(rootT->left) >= 0){ //(平衡因子>1) => 左子樹高,前面必刪右邊; (左子節點平衡因子>=0的話) => 右轉後必變為-1或0,故不用先轉; {LL}
return rotate(rootT, 'R'); //自己 (目前子樹) 右轉
}
if(bf > 1 && getBF(rootT->left) 1) => 左子樹高,前面必刪右邊; (左子節點平衡因子 右轉後必變為-2,故要先轉; {LR}
rootT->left = rotate(rootT->left, 'L'); //自己的左邊 (目前子樹的左子樹) 先左轉
return rotate(rootT, 'R'); //自己 (目前子樹) 再右轉
}
if(bf right) 右子樹高,前面必刪左邊; (右子節點平衡因子 左轉後必變為1或0,故不用先轉; {RR}
return rotate(rootT, 'L'); //自己 (目前子樹) 左轉
}
if(bf right) > 0){ //(平衡因子 右子樹高,前面必刪左邊; (右子節點平衡因子>0的話) => 左轉後必變為2,故要先轉; {RL}
rootT->right = rotate(rootT->right, 'R'); //自己的右邊 (目前子樹的右子樹) 先右轉
return rotate(rootT, 'L'); //自己 (目前子樹) 再左轉
}
return rootT;
}
public:
Node *root;
AVLTree(){ root = NULL; }
void insert(int val) {
root = insertRecursive(root, val);
}
void remove(int val) {
root = removeRecursive(root, val);
}
//前序印出AVL樹
void preOrder(Node* root) {
if(root) {
cout data left? (cout left->data right? (cout right->data left);
preOrder(root->right);
}
}
};
int main(void){
AVLTree AVLTree;
int val;
char SENT;
//對AVL樹的操作
cout " > SENT;
while(SENT != 'q'){
switch(SENT) {
case '1':
cout > val;
AVLTree.insert(val);
break;
case '2':
cout > val;
AVLTree.remove(val);
break;
case '3':
cout " > SENT;
}
system("pause");
return 0;
}

文章分享

如果這篇文章對你有幫助,歡迎分享給更多人!

[C/C++] AVL樹,插入及刪除
https://linziyou.info/posts/2019-11-26-c-c-avl樹插入及刪除/
作者
Lin Ziyou
發布於
2019-11-26
許可協議
CC BY-NC-SA 4.0
最後更新於 2019-11-26,距今已過 2288 天

部分內容可能已過時

Profile Image of the Author
Lin Ziyou
Hi! I'm Jerry~
分類
標籤
站點統計
文章
45
分類
8
標籤
10
總字數
43,470
運作天數
0
最後活動
0 天前

目錄