1. 什么是平衡二叉樹
平衡二叉樹,我們也稱【二叉平衡搜索樹/AVL】,樹中任何節點的兩個子樹的高度最大差別為1,巴拉巴拉。。。(https://baike.baidu.com/item/AVL樹/10986648?fr=aladdin)
但是有個注意的點: 平衡二叉樹的前提是 二叉排序樹(https://baike.baidu.com/item/二叉搜索樹/7077855?fr=aladdin)
這篇博客主要總結平衡二叉樹,所以,二叉排序樹知識不會提及,但是會用到。
如果想要看 排序二叉樹調整為 平衡二叉樹 旋轉相關內容的,調整至 第5節。
平衡二叉樹
非平衡二叉樹
最小不平衡子樹節點為 130
左子樹深度為 1,右子樹深度為3 ,其差值大于1,所以不平衡
2. 如何判斷二叉樹最小不平衡子樹
最小不平衡子樹為 130 這顆子樹(黃色標注)
判定最小不平衡子樹的關鍵就在于,判斷這棵樹的左子樹 和 右字數 深度之差是否大于1,若大于1 ,則證明該樹不平衡
檢查二叉樹是否平衡函數代碼實現
typedef struct {
int data; // 數據節點
struct TreeNode *left; // 指向左子樹
struct TreeNode *right; // 指向右子樹
} TreeNode , *PTreeNode;
// 記錄平衡二叉樹
bool BalanceTrue = false;
// 最小不平衡子樹地址
TreeNode *rjt = NULL;
// 檢查二叉樹是否平衡,若不平衡 BalanceTrue 為 true
int checkTreeBalance(TreeNode *root) {
if (NULL == root) { return 0; }
int x = checkTreeBalance(root->left);
int y = checkTreeBalance(root->right);
// 若檢測到最小不平衡二叉樹后,不進行后面的檢查
if (BalanceTrue) return 0;
int xx = abs(x-y);
if (xx > 1) {
// 左子樹 和 右子樹 相差大于1 , 二叉樹不平衡
BalanceTrue = true;
rjt = root;
}
return (x>y?x+1:y+1);
}
程序執行結果
# gcc -w -g -std=c11 BalanceTree.c
#
# ./a.out
當前二叉樹遍歷
前序遍歷: 580 130 80 160 150 158 210 1590 900 2100 1900
中序遍歷: 80 130 150 158 160 210 580 900 1590 1900 2100
二叉樹不平衡,不平衡子樹根節點為: 130
#
3. 二叉樹不平衡情況
在一顆平衡二叉樹的前提下,插入和刪除一個節點,都有可能會引起二叉樹不平衡,不平衡的情況主要有以下四種
左左更高
左右更高
右左更高
右右更高
4. 判斷不平衡二叉樹哪邊高
如上圖紅色所示,可以先根據最小不平衡二叉樹左子樹或者右子樹高,上圖所示,為右子樹高,則將最小不平衡二叉樹的右子樹作為樹根節點,繼續判斷子樹的左子樹或者右子樹高。
比如上圖的結果是右左較高,若進行調整的話,為先讓不平衡子樹右節點的樹先向右旋轉,然后再向左旋轉。
判斷不平衡二叉樹哪邊高代碼實現
typedef struct {
int data; // 數據節點
struct TreeNode *left; // 指向左子樹
struct TreeNode *right; // 指向右子樹
} TreeNode , *PTreeNode;
// 記錄平衡二叉樹
bool BalanceTrue = false;
// 最小不平衡子樹地址
TreeNode *rjt = NULL;
// 返回二叉樹樹高
int treeHeight(TreeNode *root) {
if (NULL == root) return 0;
int ll = treeHeight(root->left);
int rr = treeHeight(root->right);
return (ll>rr?ll+1:rr+1);
}
int main() {
/* 構建二叉樹
判斷平衡,獲取最小不平衡子樹, 將數據存入 rjt 中
輸出二叉樹 前序/中序
*/
if (BalanceTrue) {
printf("二叉樹不平衡,不平衡子樹根節點為: %d
",rjt->data);
} else {
return 0;
};
int ll = treeHeight(rjt->left);
int rr = treeHeight(rjt->right);
if (1 < ll - rr) {
printf("左子樹高
");
TreeNode *rjt_ll = rjt->left;
int child_ll = treeHeight(rjt_ll->left);
int child_rr = treeHeight(rjt_ll->right);
if (child_ll > child_rr) {
printf("左子樹更高
");
} else if (child_rr > child_ll) {
printf("右字數更高");
}
} else if (1 < rr - ll) {
printf("右子數高
");
TreeNode *rjt_rr = rjt->right;
int child_ll = treeHeight(rjt_rr->left);
int child_rr = treeHeight(rjt_rr->right);
if (child_ll > child_rr) {
printf("左子樹更高
");
} else if (child_rr > child_ll) {
printf("右字數更高");
}
}
return 0;
}
輸出
# gcc BalanceTree.c -w -g -std=c11
#
# ./a.out
當前二叉樹遍歷
前序遍歷:130 80 160 150 158 210
中序遍歷:80 130 150 158 160 210
二叉樹不平衡,不平衡子樹根節點為: 130
右子數高
左子樹更高
#
5. 如何調整平衡二叉樹
所謂的旋轉,其實是修改指針指向的值,僅此而已。
二叉樹不平衡有四種情況
左左更高
原始二叉樹,若要調整為平衡二叉樹,需要整棵樹向右旋轉
調整1:整棵樹向右旋轉
左右更高
原始二叉樹,若要調整為平衡二叉樹,需要先讓不平衡子樹左節點先向左旋轉,然后再向右旋轉
調整1: 先讓不平衡子樹左節點的樹先向左旋轉
調整2:整棵樹向右
右左更高
原始二叉樹,若要調整為平衡二叉樹,需要先讓不平衡子樹右節點的樹先向右旋轉,然后再向左旋轉
調整1: 先讓不平衡子樹右節點的樹先向右旋轉
調整2:整棵樹向左
右右更高
原始二叉樹,若要調整為平衡二叉樹,需要整棵樹向左旋轉
調整1:整棵樹向左旋轉
全部代碼
# include
# include
# include
# include
typedef struct {
int data; // 數據節點
struct TreeNode *left; // 指向左子樹
struct TreeNode *right; // 指向右子樹
} TreeNode , *PTreeNode;
// 記錄平衡二叉樹
bool BalanceTrue = false;
// 最小不平衡子樹地址
TreeNode *rjt = NULL;
// 檢查二叉樹是否平衡,若不平衡 BalanceTrue 為 true
int checkTreeBalance(TreeNode *root) {
if (NULL == root) { return 0; }
int x = checkTreeBalance(root->left);
int y = checkTreeBalance(root->right);
// 若檢測到最小二叉樹后,不進行后面的檢查
if (BalanceTrue) return 0;
int xx = abs(x-y);
if (xx > 1) {
// 左子樹 和 右子樹 相差大于1 , 二叉樹不平衡
BalanceTrue = true;
rjt = root;
}
return (x>y?x+1:y+1);
}
// 返回二叉樹樹高
int treeHeight(TreeNode *root) {
if (NULL == root) return 0;
int ll = treeHeight(root->left);
int rr = treeHeight(root->right);
return (ll>rr?ll+1:rr+1);
}
// 父節點查詢
TreeNode* queryTopData(TreeNode *root,int data) {
// 空地址異常拋出
if (NULL == root) return NULL;
// top: 父節點 ,如果為Null, 該節點為父節點
// tmp: 遍歷查詢節點
TreeNode *top = NULL;
TreeNode *tmp = root;
while (tmp != NULL) {
if (data == tmp->data) {
// 節點為 返回Null
if (NULL == top) return NULL;
return top;
}
top = tmp;
if (data > tmp->data) {
tmp = tmp->right;
} else if (data < tmp->data) {
tmp = tmp->left;
}
}
return NULL;
}
// 左左旋轉
//
// 不平衡二叉樹
// 70
// /
// 50 80
// /
// 40 60
// /
// 30
//
// 旋轉后平衡二叉樹(向右旋轉)
//
// 50
// /
// 40 70
// / /
//30 60 80
//
bool turnLL(TreeNode **root , TreeNode *notBalanceRoot) {
if ((*root) != notBalanceRoot) {
printf("左左旋轉,非根節點
");
// 非根節點
TreeNode *lleft = notBalanceRoot->left;
TreeNode *lright = lleft->right;
// 查找父節點
TreeNode *fdata = queryTopData((*root),notBalanceRoot->data);
if (NULL == fdata) return false;
lleft->right = notBalanceRoot;
notBalanceRoot->left = lright;
if (notBalanceRoot == fdata->left) {
fdata->left = lleft;
} else if (notBalanceRoot == fdata->right) {
fdata->right = lleft;
}
return true;
} else {
// 根節點
printf("左左旋轉,是根節點
");
TreeNode *lleft = notBalanceRoot->left;
TreeNode *absroot = lleft;
TreeNode *lright = lleft->right;
lleft->right = notBalanceRoot;
notBalanceRoot->left = lright;
(*root) = absroot;
return true;
}
}
// 左右旋轉
//不平衡二叉樹
// 70
// /
// 50 80
// /
// 40 60
// /
// 55
//
//左子樹向左
// 70
// /
// 60 80
// /
// 50
// /
// 40 55
//
//
// 整棵樹向右
//
// 60
// /
// 50 70
// /
// 40 55 80
//
bool turnLR(TreeNode **root , TreeNode *notBalanceRoot) {
if ((*root) != notBalanceRoot) {
printf("左右旋轉,非根節點");
TreeNode *lleft = notBalanceRoot->left;
TreeNode *leftRight = lleft->right;
TreeNode *leftRightLeft = leftRight->left;
// 第一次調整
leftRight->left = lleft;
lleft->right = leftRightLeft;
notBalanceRoot->left = leftRight;
// 查找父節點
TreeNode *fdata = queryTopData((*root),notBalanceRoot->data);
//if (NULL != fdata) printf("fdata: %d
",fdata->data);
// 第二次調整
lleft = notBalanceRoot->left;
leftRight = lleft->right;
lleft->right = notBalanceRoot;
notBalanceRoot->left = leftRight;
if (notBalanceRoot == fdata->left) {
fdata->left = lleft;
} else if (notBalanceRoot == fdata->right) {
fdata->right = lleft;
}
} else {
printf("左右旋轉,是根節點
");
TreeNode *lleft = notBalanceRoot->left;
TreeNode *leftRight = lleft->right;
TreeNode *leftRightLeft = leftRight->left;
// 第一次調整
leftRight->left = lleft;
lleft->right = leftRightLeft;
notBalanceRoot->left = leftRight;
// 第二次調整
lleft = notBalanceRoot->left;
leftRight = lleft->right;
lleft->right = notBalanceRoot;
notBalanceRoot->left = leftRight;
(*root) = lleft;
}
}
// 右左旋轉
//不平衡二叉樹
// 70
// /
// 50 80
// /
// 75 88
//
// 77
//
//左子樹向右
// 70
// /
// 50 75
// /
// 77 80
//
// 88
//
//
//
//整棵樹向左
// 75
// /
// 70 80
// /
// 50 77 88
//
bool turnRL(TreeNode **root , TreeNode *notBalanceRoot) {
TreeNode *rright = notBalanceRoot->right;
TreeNode *rightLeft = rright->left;
TreeNode *rightLeftRight = rightLeft->right;
// 第一次調整
rightLeft->right = rright;
rright->left = rightLeftRight;
notBalanceRoot->right = rightLeft;
// 查找父節點
TreeNode *fdata = queryTopData((*root),notBalanceRoot->data);
//if (NULL != fdata) printf("fdata: %d
",fdata->data);
// 第二次調整
rright = notBalanceRoot->right;
rightLeft = rright->left;
rright->left = notBalanceRoot;
notBalanceRoot->right = rightLeft;
if ((*root) != notBalanceRoot) {
printf("右左旋轉,非根節點
");
if (notBalanceRoot == fdata->left) {
fdata->left = rright;
} else if (notBalanceRoot == fdata->right) {
fdata->right = rright;
}
} else {
printf("右左旋轉,是根節點
");
(*root) = rright;
}
}
// 右右旋轉
//
// 不平衡二叉樹
// 70
// /
//50 80
// /
// 75 88
// /
// 85
//
//
//
//向左旋轉
// 80
// /
// 70 88
// / /
//50 75 85
bool turnRR(TreeNode **root , TreeNode *notBalanceRoot) {
if ((*root) != notBalanceRoot) {
printf("右右旋轉,非根節點");
TreeNode *rright = notBalanceRoot->right;
TreeNode *rleft = rright->left;
// 查找父節點
TreeNode *fdata = queryTopData((*root),notBalanceRoot->data);
if (NULL != fdata) printf("fdata: %d
",fdata->data);
rright->left = notBalanceRoot;
notBalanceRoot->right = rleft;
if (notBalanceRoot == fdata->left) {
fdata->left = rright;
} else if (notBalanceRoot == fdata->right) {
fdata->right = rright;
}
} else {
// 右右旋轉,是根節點
printf("右右旋轉,是根節點
");
TreeNode *rright = notBalanceRoot->right;
TreeNode *absroot = rright;
TreeNode *rleft = rright->left;
rright->left = notBalanceRoot;
notBalanceRoot->right = rleft;
(*root) = absroot;
}
}
// 二叉樹前序遍歷
void Print1(TreeNode *root) {
if (NULL == root) return;
printf("%d ",root->data);
Print1(root->left);
Print1(root->right);
}
// 二叉樹中序遍歷
void Print2(TreeNode *root) {
if (NULL == root) return;
Print2(root->left);
printf("%d ",root->data);
Print2(root->right);
}
// 二叉樹后續遍歷
void Print3(TreeNode *root) {
if (NULL == root) return;
Print3(root->left);
Print3(root->right);
printf("%d ",root->data);
}
// 插入二叉樹節點
TreeNode* addNode(TreeNode *root,int data) {
if (NULL == root) {
// 頭節點插入
TreeNode *Node = (TreeNode *)malloc(sizeof(TreeNode));
if (NULL == Node) {
printf("新節點申請內存失敗
");
return NULL;
}
Node->data = data;
return Node;
}
TreeNode *tmp = root;
TreeNode *top = NULL;
// 找到合適的最尾巴節點
while (NULL != tmp) {
top = tmp;
if (tmp->data == data) {
printf("已經存在該節點,節點地址: %p
",tmp);
return root;
}
if (tmp->data < data) {
tmp = tmp->right;
} else if (tmp->data > data) {
tmp = tmp->left;
}
}
TreeNode *Node = (TreeNode *)malloc(sizeof(TreeNode));
Node->data = data;
if (NULL == Node) {
printf("申請新節點內存失敗
");
return root;
}
// 鏈接節點
if (data > top->data) {
top->right = Node;
} else if (data < top->data) {
top->left = Node;
}
return root;
}
// 刪除二叉排序樹節點
bool DeleteTreeNode(TreeNode **TreeRoot,int data) {
if (NULL == (*TreeRoot)) return false;
printf("刪除節點: %d
",data);
TreeNode *tmp = (*TreeRoot);
TreeNode *top = NULL;
while (tmp != NULL) {
if (tmp->data == data) {
// 葉子節點
if ((NULL == tmp->left) && (NULL == tmp->right)) {
// 葉子節點
if (NULL == top) {
// 僅有根節點的葉子節點
free(tmp);
return true;
} else {
// 其他的葉子節點
TreeNode *lastNode = top;
if (tmp == lastNode->left) {
lastNode->left = NULL;
} else if (tmp == lastNode->right) {
lastNode->right = NULL;
}
free(tmp);
return true;
}
} else {
// 非葉子節點
// 算法為:
// 默認算法為: 1. 當刪除該節點時,獲取該樹右子樹最左子節點
// 2. 當右子樹為空時,此時應該獲取左子樹最右端子節點
if (NULL != tmp->right) {
// 方案 1
TreeNode *tmp2 = tmp->right;
TreeNode *top2 = NULL;
// 找到最后一個節點
while (tmp2->left != NULL) {
top2 = tmp2;
tmp2 = tmp2->left;
}
// 刪除老的節點
tmp->data = tmp2->data;
// 只有右子樹節點 沒有左子樹節點
if (NULL == top2) {
tmp->right = NULL;
} else {
top2->left = NULL;
}
free(tmp2);
} else {
// 方案 2
TreeNode *tmp2 = tmp->left;
TreeNode *top2 = NULL;
// 找到最后一個節點
while (tmp2->right != NULL) {
tmp2 = tmp2->right;
}
// 刪除老的節點
tmp->data = tmp2->data;
if (NULL == top2) {
tmp->left = NULL;
} else {
top2->right = NULL;
}
free(tmp2);
}
}
} else {
top = tmp;
if (data > tmp->data) {
tmp = tmp->right;
} else {
tmp = tmp->left;
}
}
}
return false;
}
// 二叉樹平衡調整
bool treeBalance(TreeNode **root) {
checkTreeBalance((*root));
while (BalanceTrue) {
printf("二叉樹不平衡,最小不平衡子樹數據結點: %d
",rjt->data);
TreeNode *tmp;
if (1 < treeHeight(rjt->left) - treeHeight(rjt->right)) {
// 對于不平衡二叉樹而言,左子樹比右子樹高
//
//printf("左
");
if (rjt->left != NULL) {
tmp = rjt->left;
int ll = treeHeight(tmp->left);
int rr = treeHeight(tmp->right);
if (ll > rr) {
// 對于不平衡子樹 左子樹 而言, 左子樹比右子樹高
// 左左旋轉
turnLL(root,rjt);
} else {
// 對于不平衡子樹 左子樹 而言, 右子樹比左子樹高
// 左右旋轉
//
turnLR(root ,rjt);
}
}
} else if (1 < treeHeight(rjt->right) - treeHeight(rjt->left)) {
// 對于不平衡二叉樹而言,右子樹比左子樹高
//
//printf("右
");
if (rjt->right != NULL) {
tmp = rjt->right;
int ll = treeHeight(tmp->left);
int rr = treeHeight(tmp->right);
if (ll > rr) {
//右左旋轉
turnRL(root,rjt);
} else {
//右右旋轉
turnRR(root,rjt);
}
}
}
BalanceTrue = false;
checkTreeBalance((*root));
printf("二叉樹調整平衡后數據結點:
");
printf("前序遍歷:");
Print1(*root);
printf("
");
printf("中序遍歷:");
Print2(*root);
printf("
");
printf("
");
}
}
int main() {
TreeNode *root = NULL;
printf("平衡二叉樹插入測試
");
int nums[] = {65,60,70,55,40,63,69,66,68,77};
int i;
for (i=0;i<sizeof(nums)/sizeof(int);i++) {
printf("插入數據: %d
",nums[i]);
root = addNode(root,nums[i]);
if (NULL == root) {
printf("首節點申請失敗");
return -1;
}
treeBalance(&root);
sleep(1);
}
printf("
當前二叉樹遍歷
");
printf("前序遍歷:");
Print1(root);
printf("
");
printf("中序遍歷:");
Print2(root);
printf("
");
//return 0;
printf("
平衡二叉樹刪除測試
");
for (i=2;i<5;i++) {
DeleteTreeNode(&root,nums[i]);
treeBalance(&root);
sleep(1);
}
printf("
當前二叉樹遍歷
");
printf("前序遍歷:");
Print1(root);
printf("
");
printf("中序遍歷:");
Print2(root);
printf("
");
return 0;
}
程序執行結果
# gcc BalanceTree.c -w -g -std=c11
#
# ./a.out
平衡二叉樹插入測試
插入數據: 65
插入數據: 60
插入數據: 70
插入數據: 55
插入數據: 40
二叉樹不平衡,最小不平衡子樹數據結點: 60
左左旋轉,非根節點
二叉樹調整平衡后數據結點:
前序遍歷:65 55 40 60 70
中序遍歷:40 55 60 65 70
插入數據: 63
二叉樹不平衡,最小不平衡子樹數據結點: 65
左右旋轉,是根節點
二叉樹調整平衡后數據結點:
前序遍歷:60 55 40 65 63 70
中序遍歷:40 55 60 63 65 70
插入數據: 69
插入數據: 66
二叉樹不平衡,最小不平衡子樹數據結點: 70
左左旋轉,非根節點
二叉樹調整平衡后數據結點:
前序遍歷:60 55 40 65 63 69 66 70
中序遍歷:40 55 60 63 65 66 69 70
插入數據: 68
二叉樹不平衡,最小不平衡子樹數據結點: 65
右左旋轉,非根節點
二叉樹調整平衡后數據結點:
前序遍歷:60 55 40 66 65 63 69 68 70
中序遍歷:40 55 60 63 65 66 68 69 70
插入數據: 77
二叉樹不平衡,最小不平衡子樹數據結點: 60
右右旋轉,是根節點
二叉樹調整平衡后數據結點:
前序遍歷:66 60 55 40 65 63 69 68 70 77
中序遍歷:40 55 60 63 65 66 68 69 70 77
當前二叉樹遍歷
前序遍歷:66 60 55 40 65 63 69 68 70 77
中序遍歷:40 55 60 63 65 66 68 69 70 77
平衡二叉樹刪除測試
刪除節點: 70
刪除節點: 55
刪除節點: 40
二叉樹不平衡,最小不平衡子樹數據結點: 60
右左旋轉,非根節點
二叉樹調整平衡后數據結點:
前序遍歷:66 63 60 65 69 68 77
中序遍歷:60 63 65 66 68 69 77
當前二叉樹遍歷
前序遍歷:66 63 60 65 69 68 77
中序遍歷:60 63 65 66 68 69 77
#
審核編輯:湯梓紅
-
C語言
+關注
關注
180文章
7614瀏覽量
137702 -
二叉樹
+關注
關注
0文章
74瀏覽量
12373
原文標題:平衡二叉樹 C語言代碼實現
文章出處:【微信號:技術讓夢想更偉大,微信公眾號:技術讓夢想更偉大】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論