AVL树(查找、插入、删除)——C语言
AVL树
平衡二叉查找树(Self-balancing binary search tree)又被称为AVL树(AVL树是根据它的发明者G. M. Adelson-Velskii和E. M. Landis命名的),是在二叉查找树的基础上一个优化版本
AVL树的特点:
1.本身首先是一棵二叉查找树 2.带有平衡条件:每个结点的左右子树的高度之差的绝对值不超过1,也就是说,AVL树,本质上是带了平衡功能的二叉查找树 如果读者关于二叉查找树还不了解可以看一下这篇随笔:二叉查找树(查找、插入、删除)
AVL树的作用
AVL树解决了二叉查找树可能出现的极端情况,对于一般的二叉搜索树(Binary Search Tree),其期望高度(即为一棵平衡树时)为log2n,其各操作的时间复杂度(O(log2n))同时也由此而决定,但是在某些极端情况下
(如在插入的序列是有序的时),二叉搜索树将退化成近似链或链,此时,其操作的时间复杂度将退化成线性的,即O(n)。我们可以通过随机化建立二叉搜索树来尽量的避免这种情况,但是在进行了多次的操作之后,例如在在删除时,
我们总是选择将待删除节点的后继代替它本身,这样就会造成总是右边的节点数目减少,以至于树向左偏沉。这同时也会造成树的平衡性受到破坏,使得它的操作时间复杂度增加
例如下面这种情况:
/* 查找特定值 */ void SearchData(int targ, BSTree *nod) { if (nod != NULL) { if (nod->data == targ) { printf("查找值存在,值为%d\n", nod->data); } else if (nod->data > targ) { SearchData(targ, nod->left); //递归查找左子树 } else if (nod->data < targ) { SearchData(targ, nod->right); //递归查找右子树 } } else if (nod == NULL) { printf("查找值不存在\n"); } }View Code
2、插入节点(递归实现)
先梳理一下步骤
/* RR型旋转 */ AVLTree * RR_Rotation(AVLTree *nod) { AVLTree *temp; temp = nod->right; //临时保存nod的右子树 nod->right = nod->right->left; temp->left = nod; nod->height = GetNodeHeight(nod); //更新节点高度 temp->height = GetNodeHeight(temp); return temp; }View Code
第三种:LR型
LR型失衡的操作相比于LL型失衡操作相对要复杂一点,需要旋转两次才能恢复平衡
第一步:对失衡节点的左子树进行RR型旋转
第二步:对失衡节点进行LL型旋转
因为之前已经写好了LL型和RR型的旋转,这里直接用就可以了,实现代码如下
/* LR型旋转 */ AVLTree * LR_Rotation(AVLTree *nod) { nod->left = RR_Rotation(nod->left); nod = LL_Rotation(nod); return nod; }
LR型旋转图解:
/* RL型旋转 */ AVLTree * RL_Rotation(AVLTree *nod) { nod->right = LL_Rotation(nod->right); nod = RR_Rotation(nod); return nod; }View Code
3、删除节点
删除节点比插入节点的操作还要稍微复杂一点,因为插入时,进行一次平衡处理(一次平衡处理可能包含多次旋转),整棵树都会处于平衡状态,而在删除时,需要进行多次平衡处理,才能保证树处于平衡状态
AVL树的删除操作前半部分和二叉查找树相同,只不过删除后要检查树是否失去平衡,如果失衡就需要重新调整平衡,并更新节点高度,总的来说可以分为如下几种情况
(1)删除叶子节点
情况一:删除节点后二叉树没有失去平衡
删除节点后树没有失去平衡,这种情况下只需要更新节点的高度
情况二:删除节点后二叉树失去平衡
上图的RE型失衡只有在删除操作时才可能出现(在插入时不可能出现),RE型失衡的旋转方式和RR型失衡的旋转方式一模一样
(虽然删除节点时遇到的失衡情况多了两种 LE和RE ,但是旋转的方式依旧是那四种(LL、RR、LR、RL))
实现代码:
/* 删除节点 */ AVLTree *DeletNode(AVLTree *nod, int DelData) { AVLTree *SNode = NULL; //后继节点 AVLTree *PSNode = NULL; //后继节点的父节点 AVLTree *temp = NULL; //临时保存待释放节点的子树,避免free后找不到左右子树 if (nod == NULL) { printf("删除节点不存在"); exit(0); } else if (DelData > nod->data) { nod->right = DeletNode(nod->right, DelData); if (GetNodeHeight(nod->left) - GetNodeHeight(nod->right) > 1) { temp = nod->left; if (GetNodeHeight(temp->left) >= GetNodeHeight(temp->right)) //LL型或LE型失衡、两种情况处理方式相同 { nod = LL_Rotation(nod); } else //LR型失衡 { nod = LR_Rotation(nod); } } nod->height = GetNodeHeight(nod); //更新节点高度 } else if (DelData < nod->data) { nod->left = DeletNode(nod->left, DelData); if (GetNodeHeight(nod->right) - GetNodeHeight(nod->left) > 1) { temp = nod->right; if (GetNodeHeight(temp->right) >= GetNodeHeight(temp->left)) //RR或RE型失衡、两种情况处理方式相同 { nod = RR_Rotation(nod); } else //RL型失衡 { nod = RL_Rotation(nod); } } nod->height = GetNodeHeight(nod); //更新节点高度 } else if (DelData == nod->data) { if (nod->right == NULL && nod->left == NULL) //若待删除节点为叶子节点 { free(nod); return NULL; } }
(2)删除带有一个子节点的节点
else if (DelData == nod->data) { if (nod->right == NULL && nod->left == NULL) //若待删除节点为叶子节点 { free(nod); return NULL; } else if (nod->right == NULL && nod->left != NULL) //若待删除节点只有左子树 { temp = nod->left; free(nod); return temp; } else if (nod->right != NULL && nod->left == NULL) //若待删除节点只有右子树 { temp = nod->right; free(nod); return temp; } }
(3)删除带有两个子节点的节点
删除带有两个子节点的节点时,需要找到待删除的节点的后继节点或者前驱节点(本篇随笔使用后继节点),具体方法在上一篇随笔已经列出“二叉查找树(查找、插入、删除)——C语言”,这里不再赘述
else //若待删除节点既有左子树也有右子树 { SNode = SearchSuccessorNode(nod->right); //搜索后继节点 PSNode = SearchParentofSNode(nod->right, nod->right); //搜索后继节点的父节点 if (nod->right == SNode) //后继节点为待删除节点的右子树(后继节点有右子树和没有右子树的操作相同) { SNode->left = nod->left; free(nod); return SNode; } else if (nod->right != SNode && SNode->right == NULL) //后继节点不为待删除节点的右子树,并且该后继节点没有右子树 { SNode->left = nod->left; SNode->right = nod->right; PSNode->left = NULL; free(nod); return SNode; } else if (nod->right != SNode && SNode->right != NULL) //后继节点不为待删除节点的右子树,并且该后继节点有右子树 { PSNode->left = SNode->right; //后继节点的右子树作为后继节点父节点的左子树 SNode->left = nod->left; SNode->right = nod->right; free(nod); return SNode; } } }
需要注意的是,删除节点时不会出现“后继节点不是删除节点的子节点,且后继节点有右子树”这种情况,如下图
上图的14节点已经失衡了,在插入的时候就会被调整,所以不会出现“后继节点不是删除节点的子节点,且后继节点有右子树”
(由于笔者能力有限,AVL树的删除操作分析的不是很清楚,若有疏漏,清指出)
- C语言实现二叉查找树(搜索树)的创建,插入,查找,删除
- C语言实现带头结点的链表的创建、查找、插入、删除操作
- objecttive-C语言使用二叉排序树实现查找、插入、删除、查看元素
- 二叉查找树的查找、插入、删除、释放等基本操作的实现(C语言)
- c语言实现二叉树的插入、查找、删除、打印树
- 平衡二叉树的C语言实现(创建、插入、查找、删除、旋转)【数据结构】
- 二叉查找树(二叉排序树)创建、插入、删除、查找-C语言
- C语言单链表的创建、插入、查找、删除、求长、排序、遍历
- B树之C语言实现(包括查找、删除、插入)
- C语言---单链表的插入、删除、查找操作
- 二叉排序树(新建,插入,查找,删除)(C语言编写)
- c语言实现单链表建立,插入,删除,查找,循环链表,静态链表
- 链表操作 对链表进行输入,插入,删除结点,按关键字进行查找操作 C语言
- c语言:单链表的实现(一) 创建,插入,删除,查找
- 数据结构-链表操作(创建、输出、查找、插入、删除等) C语言源码
- C语言实现双链表基本操作(创建、查找、插入、删除)
- C语言---双向链表的插入、删除、查找操作
- 平衡二叉树的 插入 删除 查找 等功能c语言实现 数据结构
- B-树的插入、查找、删除 及 可执行的C语言代码
- c语言心得-----数组中对元素的操作排序,查找,插入,和删除