赞
踩
关注、星标公众号,直达精彩内容
来源: https://www.cnblogs.com/NoneID
整理:李肖遥
平衡二叉树,我们也称【二叉平衡搜索树/AVL】,树中任何节点的两个子树的高度最大差别为1,巴拉巴拉。。。(https://baike.baidu.com/item/AVL树/10986648?fr=aladdin)
但是有个注意的点: 平衡二叉树的前提是 二叉排序树(https://baike.baidu.com/item/二叉搜索树/7077855?fr=aladdin)
这篇博客主要总结平衡二叉树,所以,二叉排序树知识不会提及,但是会用到。
如果想要看 排序二叉树调整为 平衡二叉树 旋转相关内容的,调整至 第5节。
平衡二叉树
非平衡二叉树
最小不平衡子树节点为 130
左子树深度为 1,右子树深度为3 ,其差值大于1,所以不平衡
最小不平衡子树为 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
- #
在一颗平衡二叉树的前提下,插入和删除一个节点,都有可能会引起二叉树不平衡,不平衡的情况主要有以下四种
左左更高
左右更高
右左更高
右右更高
如上图红色所示,可以先根据最小不平衡二叉树左子树或者右子树高,上图所示,为右子树高,则将最小不平衡二叉树的右子树作为树根节点,继续判断子树的左子树或者右子树高。
比如上图的结果是右左较高,若进行调整的话,为 先让不平衡子树右节点的树先向右旋转,然后再向左旋转。
判断不平衡二叉树哪边高代码实现
- 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\n",rjt->data);
- } else {
- return 0;
- };
-
-
- int ll = treeHeight(rjt->left);
- int rr = treeHeight(rjt->right);
- if (1 < ll - rr) {
- printf("左子树高\n");
- 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("左子树更高\n");
- } else if (child_rr > child_ll) {
- printf("右字数更高");
- }
-
- } else if (1 < rr - ll) {
- printf("右子数高\n");
- 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("左子树更高\n");
- } 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
- 右子数高
- 左子树更高
- #
所谓的旋转,其实是修改指针指向的值,仅此而已。
二叉树不平衡有四种情况
左左更高
原始二叉树,若要调整为平衡二叉树,需要整棵树向右旋转
调整1:整棵树向右旋转
左右更高
原始二叉树,若要调整为平衡二叉树,需要 先让不平衡子树左节点先向左旋转,然后再向右旋转
调整1: 先让不平衡子树左节点的树先向左旋转
调整2: 整棵树向右
右左更高
原始二叉树,若要调整为平衡二叉树,需要 先让不平衡子树右节点的树先向右旋转,然后再向左旋转
调整1: 先让不平衡子树右节点的树先向右旋转
调整2: 整棵树向左
右右更高
原始二叉树,若要调整为平衡二叉树,需要 整棵树向左旋转
调整1: 整棵树向左旋转
- # include <stdio.h>
- # include <stdbool.h>
- # include <stdlib.h>
- # include <math.h>
-
- 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("左左旋转,非根节点\n");
- // 非根节点
- 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("左左旋转,是根节点\n");
- 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\n",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("左右旋转,是根节点\n");
-
- 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\n",fdata->data);
-
- // 第二次调整
- rright = notBalanceRoot->right;
- rightLeft = rright->left;
-
- rright->left = notBalanceRoot;
- notBalanceRoot->right = rightLeft;
-
- if ((*root) != notBalanceRoot) {
- printf("右左旋转,非根节点\n");
- if (notBalanceRoot == fdata->left) {
- fdata->left = rright;
- } else if (notBalanceRoot == fdata->right) {
- fdata->right = rright;
- }
-
- } else {
- printf("右左旋转,是根节点\n");
- (*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\n",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("右右旋转,是根节点\n");
- 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\t",root->data);
- Print1(root->left);
- Print1(root->right);
- }
-
- // 二叉树中序遍历
- void Print2(TreeNode *root) {
- if (NULL == root) return;
- Print2(root->left);
- printf("%d\t",root->data);
- Print2(root->right);
- }
-
- // 二叉树后续遍历
- void Print3(TreeNode *root) {
- if (NULL == root) return;
- Print3(root->left);
- Print3(root->right);
- printf("%d\t",root->data);
- }
-
- // 插入二叉树节点
- TreeNode* addNode(TreeNode *root,int data) {
- if (NULL == root) {
- // 头节点插入
- TreeNode *Node = (TreeNode *)malloc(sizeof(TreeNode));
- if (NULL == Node) {
- printf("新节点申请内存失败\n");
- return NULL;
- }
- Node->data = data;
-
- return Node;
- }
-
- TreeNode *tmp = root;
- TreeNode *top = NULL;
-
- // 找到合适的最尾巴节点
- while (NULL != tmp) {
- top = tmp;
- if (tmp->data == data) {
- printf("已经存在该节点,节点地址: %p\n",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("申请新节点内存失败\n");
- 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\n",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\n",rjt->data);
- TreeNode *tmp;
-
- if (1 < treeHeight(rjt->left) - treeHeight(rjt->right)) {
- // 对于不平衡二叉树而言,左子树比右子树高
- //
- //printf("左\n");
- 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("右\n");
- 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("二叉树调整平衡后数据结点:\n");
- printf("前序遍历:");
- Print1(*root);
- printf("\n");
- printf("中序遍历:");
- Print2(*root);
- printf("\n");
- printf("\n");
- }
-
- }
-
- int main() {
- TreeNode *root = NULL;
-
- printf("平衡二叉树插入测试\n");
- 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\n",nums[i]);
-
- root = addNode(root,nums[i]);
- if (NULL == root) {
- printf("首节点申请失败");
- return -1;
- }
-
- treeBalance(&root);
- sleep(1);
-
- }
- printf("\n当前二叉树遍历\n");
- printf("前序遍历:");
- Print1(root);
- printf("\n");
- printf("中序遍历:");
- Print2(root);
- printf("\n");
- //return 0;
-
- printf("\n\n平衡二叉树删除测试\n");
-
- for (i=2;i<5;i++) {
- DeleteTreeNode(&root,nums[i]);
-
- treeBalance(&root);
- sleep(1);
- }
-
- printf("\n当前二叉树遍历\n");
- printf("前序遍历:");
- Print1(root);
- printf("\n");
- printf("中序遍历:");
- Print2(root);
- printf("\n");
-
- 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
- #
‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧
关注我的微信公众号,回复“加群”按规则加入技术交流群。
点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。