平衡二叉树 C语言代码实现
关注、星标公众号,直达精彩内容
来源: https://www.cnblogs.com/NoneID
整理:李肖遥
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\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
右子数高
左子树更高
#
5. 如何调整平衡二叉树
所谓的旋转,其实是修改指针指向的值,仅此而已。
二叉树不平衡有四种情况
左左更高
原始二叉树,若要调整为平衡二叉树,需要整棵树向右旋转
调整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) {// 节点为 返回Nullif (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) {// 方案 1TreeNode *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 {// 方案 2TreeNode *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 ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧
关注我的微信公众号,回复“加群”按规则加入技术交流群。
点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。
平衡二叉树 C语言代码实现相关推荐
- Algorithm:树相关算法(BBT/BST/B树/R树)简介(二叉查找树、二叉查找树的插入节点、二叉查找树的删除、二叉树的遍历、平衡二叉树)C 语言实现
Algorithm:树相关算法(BBT/BST/B树/R树)简介(二叉查找树.二叉查找树的插入节点.二叉查找树的删除.二叉树的遍历.平衡二叉树)C++语言实现 目录 树的基础知识 1.二叉树的遍-前序 ...
- 遗传算法c语言程序,遗传算法c语言代码.doc
遗传算法c语言代码 遗传算法代码 #include #include #include #include #include struct group //染色体的结构 { int city[citie ...
- 071_html语言代码
1. ISO语言代码 1.1. 国际标准化组织(International Organization for Standardization, ISO)简称ISO, 是一个全球性的非政府组织, 是国际 ...
- 嵌入式C语言代码规范
C语言代码规范 参考安富莱C语言编码规范 1.文件与目录 1.文件及目录的命名规定可用的字符集是[A-Z:a-z:0-9:._-]. 2.源文件名后缀用小写字母 .c 和.h. 3.文件的命名要准确清 ...
- java 与c 运行效率_Java语言与C语言代码运行效率的比较
<Java语言与C语言代码运行效率的比较>由会员分享,可在线阅读,更多相关<Java语言与C语言代码运行效率的比较(2页珍藏版)>请在人人文库网上搜索. 1.Java语言与C语 ...
- Python语言学习:python语言代码调试—异常处理之详细攻略
Python语言学习:python语言代码调试-异常处理之详细攻略 目录 python语言代码调试-异常处理 异常捕捉可以使用 try/except 语句 相关文章 Python3 错误和异常 | 菜 ...
- 编程笔试(解析及代码实现):猴子吃桃。猴子第一天吃了若干个桃子,当即吃了一半,还不解馋,又多吃了一个…的C++、Java、Python、C#等语言代码实现
编程笔试(解析及代码实现):猴子吃桃.猴子第一天吃了若干个桃子,当即吃了一半,还不解馋,又多吃了一个. 第二天早上又将剩下的桃子吃了一半,还是不过瘾,又多吃了一个.以后每天都吃前一天剩下的一半再加一个 ...
- c语言错误 xef代表什么,单片机C语言代码手册 含100多个经典C程序
1 单片机单片机 C 语言代码手册语言代码手册 1 LED 灯灯 点亮一个点亮一个 LED include void main while 1 P0 0 x01 P2 0 x7d 流水灯闪烁流水灯闪烁 ...
- c 语言怎么编译 .dll,将你的 C 语言代码编译成 .NET
介绍 通常情况下,对于那些使用C语言编程并开始用C#/ VB或一些其他的用于.NET的编译语言编程,那么他们希望或者甚至是需要调用我们用C语言编写的函数代码. 每当我在互联网上钻研,或说要在编译器中使 ...
最新文章
- 3164 质因数分解
- java9.0.1教学,零基础Java基础教程【9天入门】
- JPA中实现单向多对一的关联关系
- React ----- 路由懒加载的几种实现方案
- Exchange Server 2007迁移Exchange Server 2010 (16)--- OWA重定向
- 滑动窗口在重构数据集的作用
- 成功的人和不成功的人最大的区别
- 任何在aix下面看你的系统是32位还是64位的
- R语言︱集合运算——小而美法则
- Unity3D 游戏开发构架篇 ——输入控制
- 2. PHP 自动转义函数
- python 反弹shell,加了UDP
- unity3d面试题
- 液压与气动传动(PLC控制)
- 皮尔兹777301安全继电器
- tftpd32更新内核文件
- Win10 64位+Mysql5.7+主从同步配置
- 2021-09-29 关于间断点相关题目的总结
- Unity3d Platformer Pro 2D游戏开发框架使用教程
- 插秧诗 - 退步原来是向前