平衡二叉树(AVL)的创建、插入、删除
2017-09-11 12:24
387 查看
一、概念
AVL的优势是在查找时避免了出现一般二叉排序树查找的最坏情况。但是AVL在经过插入或删除结点的操作之后可能出现不平衡的情况,所以每次插入或删除完之后要判断是否平衡,不平衡就需要rebalance.这会增加插入删除的复杂度。
二、AVL的创建
三、AVL的插入
1.方法:
2.插入处的叶子节点的情况:
显然
3.不平衡节点的情况
!!注意:这里讨论的是三、2.4)中逐级往上,第一个出现不平衡的节点的情况。其他节点若不平衡都是依次逐级递归调整,讨论且只有讨论第一个出现的不平衡的节点的情况才有意义。
(1)插入前
显然,
(本文选择第一种,下文即根据第一种而展开)
(2)插入后
显然
(3)对于”LL型“的讨论
显然,
(3)对于”LR型“的讨论
显然
四、AVL的删除
1.方法:
2.删除处的叶子节点的情况
!!注意,此处指的是四、1.1)中递归到最后删除叶子结点的情况[/b]
显然,
3.不平衡节点的情况
!!注意:这里讨论的是三、2.4)中逐级往上,第一个出现不平衡的节点的情况。其他节点若不平衡都是依次逐级递归调整,讨论且只有讨论第一个出现的不平衡的节点的情况才有意义。
(1)删除前
显然,
1)删除后第一个出现不平衡的节点在插入前有且仅有这两种情况
2)我们只需讨论其中一种情况,另一种同理可解(本文选择第一种,下文即针对第一种情况而展开)
(2)删除后
显然,
(3)对于”LL型“的讨论
(4)对于”LR型“的讨论
(5)对于”LE型“的讨论
五、源代码
参考:1
但请注意,这个资料里举例的图存在错误
平衡二叉树(下文称AVL)是满足每个结点的左右子树高度(亦称深度)之差在{0,1,-1}这个范围内的二叉排序树(二叉排序树也叫二叉搜索树)。
AVL的优势是在查找时避免了出现一般二叉排序树查找的最坏情况。但是AVL在经过插入或删除结点的操作之后可能出现不平衡的情况,所以每次插入或删除完之后要判断是否平衡,不平衡就需要rebalance.这会增加插入删除的复杂度。
二、AVL的创建
正常的有规律的创建方法(实际上是各种资料采用的方法)是将创建, 看成从NULL开始,一次次对原有的AVL的插入(显然只有一个结点的Tree一定是AVL)。
三、AVL的插入
1.方法:
先把结点按二叉排序树的结构正常插入,再由底向上逐级判断是否平衡,不平衡就rebalance. 插一次可能经过几次rebalance。(具体情况请往下读)
2.插入处的叶子节点的情况:
图1
显然
1) 插入处的叶子有且仅有这4种情况 2)对于②④来说,一定不影响原有树平衡 3)对于①③来说,不确定是否影响原有树的平衡;如果影响,不平衡点一定在结点A的祖先结点中(包括父亲节点,下文同)。 4)对于①③来说,我们要把这种不平衡的可能逐级传递给A的祖先结点,再逐级判断A的祖先节点 a.是否不平衡,不平衡需要rebalance, 之后b.是否有影响上一级平衡性的可能,有就需要继续传递这种可能。进行递归
3.不平衡节点的情况
!!注意:这里讨论的是三、2.4)中逐级往上,第一个出现不平衡的节点的情况。其他节点若不平衡都是依次逐级递归调整,讨论且只有讨论第一个出现的不平衡的节点的情况才有意义。
(1)插入前
图2 注: LH:左子树高 RH:右子树高 EQ:左右子树等高 x:深度值
显然,
1)插入后第一个出现不平衡的节点在插入前有且仅有这两种情况 2)我们只需讨论其中一种情况,另一种同理可解
(本文选择第一种,下文即根据第一种而展开)
(2)插入后
图3 注: DISEQ_LH:因为左子树过高而不平衡 DISEQ_RH:因为右子树过高而不平衡
显然
1)第一个出现不平衡的节点有且仅有这两种情况 2)有的书籍中将第一种情况称为”LL型“,第二种情况称为"LR"型
(3)对于”LL型“的讨论
图4
显然,
1)LL型有且仅有这两种情况 2)有的书籍将第一种情况称为"LL的一般型”,第二种称为“LL的简单型”,这很明显是不科学的,甚至是错误的叫法, 因为一般型不能包含简单型还叫一般吗?本文将第一种称为”LL的次简型“,第二种称为“LL的最简型”。 3)rebalance方法如下图(二者代码一致):
图5
(3)对于”LR型“的讨论
图6
显然
1)"LR型"有且仅有这两种情况 2)本文将第一种称为”LR的次简型“,第二种称为“LR的最简型” 3)rebalance方法如下图(二者代码不一致,主要在于平衡因子的改变不同):
图7
四、AVL的删除
1.方法:
1)删除:先找到要删除的结点;然后判断结点的子树情况,如果无子树,直接删除,如果有一个孩子,删除后将孩子结点接到父亲结点上, 如果有两个孩子,可以选择a.在左孩子中找到最大结点 b.在右孩子中找到最小结点 ,将找到的节点与要删除的结点的data替换, 然后问题转化为在删除结点的左子树中删除找到的结点。变成递归。 2)出现不平衡,即进行rebalance,删除一次可能rebalance多次
2.删除处的叶子节点的情况
!!注意,此处指的是四、1.1)中递归到最后删除叶子结点的情况[/b]
图8
显然,
1)删除处的叶子有且仅有这4种情况 2)对于②④来说,一定不影响原有树平衡 3)对于①③来说,不确定是否影响原有树的平衡;如果影响,不平衡点一定在结点A的祖先结点中(包括父亲节点,下文同)。 a.是否不平衡,不平衡需要rebalance, 之后b.是否有影响上一级平衡性的可能,有就需要继续传递这种可能。进行递归。
3.不平衡节点的情况
!!注意:这里讨论的是三、2.4)中逐级往上,第一个出现不平衡的节点的情况。其他节点若不平衡都是依次逐级递归调整,讨论且只有讨论第一个出现的不平衡的节点的情况才有意义。
(1)删除前
图9
显然,
1)删除后第一个出现不平衡的节点在插入前有且仅有这两种情况
2)我们只需讨论其中一种情况,另一种同理可解(本文选择第一种,下文即针对第一种情况而展开)
(2)删除后
图10
显然,
1)第一个出现不平衡的节点有且仅有这3种情况 2)对照前面的插入操作,我们知道,第一种不平衡情况称为”LL型“,第二种情况称为"LR"型,那么第三种呢?有的教材将其归为前两种之一, 那明显是错误的,请那么做的作者去看LL、LR型的定义。有的教材更差劲,甚至不讲AVL删除操作。我将第三种情况命名为“LE型”。
(3)对于”LL型“的讨论
操作同插入时的“LL型”,但请清楚具体情况与插入时不同
(4)对于”LR型“的讨论
图11
(5)对于”LE型“的讨论
图12 注:对于”LE型“,可以用"LL型"的操作旋转节点,也可以用"LR型“的操作旋转节点。(但注意过后平衡因子的改变与LL或LR不同),应用LL显然更简单。
五、源代码
enum BalanceFactor //平衡因子取值 { DISEQ_RH = -2, //因为右子树高2不平衡 RH = -1, //右子树比左子树高1 EQ = 0, //左右子树等高 LH =1, //左子树比右子树高1 DISEQ_LH = 2 //因为左子树高2不平衡 }; typedef struct Tnode { BalanceFactor t_balancefacor; float t_data; struct Tnode* lchild; struct Tnode* rchild; }Tnode; void createBBST(Tnode* &tree); bool searchBBST(Tnode* tree, float search_data); bool insertBBST(Tnode* &tree, float insert_data, bool &taller); bool deleteBBST(Tnode* &tree, float delete_data, bool &shorter); /*注意:AVL的插入和删除是有固定方法的,虽然还有其他手段也能使得插入后的AVL树平衡,但不是我们要探讨的*/ //(1)插入:按二叉搜索树的方法插入,再调平衡。 // 此种插入方法一共有四种失衡状态:LL,LR,RR,RL //(2)删除:使用其按顺序的前导或顺序后继节点替换将要删除的节点,然后删除按顺序的前导或顺序后继节点。 // 此种删除方法就是在其左子树中找最大节点,或在右子树中找最小节点替换,(再次注意,两种方式得到的结果不同) // 然后再删除左子树中的最大节点或右子树中的最小节点, // 然后从第一个不平衡点开始,调平衡,找不平衡点,调平衡,构成递归。 // 以下提到的祖先节点包括父节点 void createBBST(Tnode* &tree) { printf("Please input a sequence of numbers for create a BBST (numbers are separated by blanks and the sequence ends with #.):\n"); float insert_data; char buffer; scanf("%f", &insert_data); scanf("%c", &buffer); //printf("%g ", insert_data); bool taller = false; while (buffer != '#'){ insertBBST(tree, insert_data,taller); scanf("%f", &insert_data); scanf("%c", &buffer); //printf("%g ", insert_data); } } bool searchBBST(Tnode* tree,float search_data) //tree中找search_data, 找到返回true { if (tree == NULL) { return false; } else { if (search_data < tree->t_data) { return searchBBST(tree->lchild, search_data); } else if (search_data > tree->t_data) { return searchBBST(tree->rchild, search_data); } else return true; } return true; } Tnode* AdjustRR(Tnode* anode) { Tnode* bnode = anode->rchild; anode->rchild = bnode->lchild; bnode->lchild = anode; anode->t_balancefacor = EQ; bnode->t_balancefacor = EQ; return bnode; } Tnode* AdjustLL(Tnode* anode) { Tnode* bnode = anode->lchild; anode->lchild = bnode->rchild; bnode->rchild = anode; anode->t_balancefacor = EQ; bnode->t_balancefacor = EQ; return bnode; } Tnode* AdjustRL(Tnode* anode) { Tnode* bnode = anode->rchild; Tnode* cnode = bnode->lchild; anode->rchild = cnode->lchild; bnode->lchild = cnode->rchild; cnode->lchild = anode; cnode->rchild = bnode; if (cnode->t_balancefacor == EQ) //最简单不平衡情况 { anode->t_balancefacor = EQ; bnode->t_balancefacor = EQ; cnode->t_balancefacor = EQ; } else//次简单不平衡情况 { if (cnode->t_balancefacor == LH) { anode->t_balancefacor = EQ; bnode->t_balancefacor = RH; cnode->t_balancefacor = EQ; } else if (cnode->t_balancefacor == RH) { anode->t_balancefacor = LH; bnode->t_balancefacor = EQ; cnode->t_balancefacor = EQ; } } return cnode; } Tnode* AdjustLR(Tnode* anode) { Tnode* bnode = anode->lchild; Tnode* cnode = bnode->rchild; anode->lchild = cnode->rchild; bnode->rchild = cnode->lchild; cnode->lchild = bnode; cnode->rchild = anode; if (cnode->t_balancefacor == EQ) //处理最简单不平衡情况 { anode->t_balancefacor = EQ; bnode->t_balancefacor = EQ; cnode->t_balancefacor = EQ; } else//处理次简单不平衡情况 { if (cnode->t_balancefacor == LH) { anode->t_balancefacor = RH; bnode->t_balancefacor = EQ; cnode->t_balancefacor = EQ; } else if (cnode->t_balancefacor == RH) { anode->t_balancefacor = EQ; bnode->t_balancefacor = LH; cnode->t_balancefacor = EQ; } } return cnode; } bool insertBBST(Tnode* &tree, float insert_data, bool &taller) //在tree中插入inser_data, 成功插入返回true { taller = false; //多赋值,可能是冗余操作,后单步知并不冗余 bool inserted = false; if (tree == NULL) { tree = (Tnode*)malloc(sizeof(Tnode)); tree->t_data = insert_data; tree->lchild = tree->rchild = NULL; tree->t_balancefacor = EQ; taller = true; inserted = true; return inserted; //return true; } else { if (insert_data < tree->t_data) { inserted = insertBBST(tree->lchild, insert_data, taller); //最下面的节点 if (taller == true) { switch (tree->t_balancefacor)//判断在插入之前tree->t_balancefactor的值 { case EQ: tree->t_balancefacor = LH; taller = true; //递归给上一级的taller break; case LH: //走到此分支均是因为增加节点之后,不平衡。此处进行rebalance,并重置balancefactor tree->t_balancefacor = DISEQ_LH; //可能出现LR,也可能LL //只能是这2种情况,因为前面有if(taller)限制,tree->lchild->balancefactor此刻就不能是EQ //换一种有趣的说法是不能一次加两个节点,所以tree->lchild->balancefactor此刻不能是EQ if (tree->lchild->t_balancefacor == RH) { /*LR类型处理并修改平衡因子*/ tree = AdjustLR(tree); taller = false; break; } else if (tree->lchild->t_balancefacor == LH) { /*LL类型处理并修改平衡因子*/ tree = AdjustLL(tree); taller = false; break; } case RH: tree->t_balancefacor = EQ; taller = false; break; } } return inserted; } else if (insert_data > tree->t_data) { inserted = insertBBST(tree->rchild, insert_data, taller); if (taller == true) { switch (tree->t_balancefacor)//判断在插入之前tree->t_balancefactor的值 { case EQ: tree->t_balancefacor = RH; taller = true; //递归给上一级的taller break; case LH: tree->t_balancefacor = EQ; taller = false; break; case RH: //走到此分支均是因为增加节点之后,不平衡。此处进行rebalance,并重置balancefactor tree->t_balancefacor = DISEQ_RH; //可能出现LR,也可能LL //只能是这2种情况,因为前面有if(taller)限制,tree->rchild->balancefactor此刻就不能是EQ //换一种有趣的说法是不能一次加两个节点,所以tree->lchild->balancefactor此刻不能是EQ if (tree->rchild->t_balancefacor == LH) { /*RL处理并修改平衡因子*/ tree = AdjustRL(tree); taller = false; break; } else if (tree->rchild->t_balancefacor == RH) { /*RR处理并修改平衡因子*/ tree = AdjustRR(tree); taller = false; break; } } } return inserted; } else { return false; //insert_data已存在,返回false } } } //虽然删除节点与插入的调整LL\LR\RR\RL的具体情况不一样,但是居然得到正确结果的操作并无不同 Tnode* AdjustLL_delete(Tnode* anode) { return AdjustLL(anode); } Tnode* AdjustRR_delete(Tnode* anode) { return AdjustRR(anode); } Tnode* AdjustLR_delete(Tnode* anode) { return AdjustLR(anode); } Tnode* AdjustRL_delete(Tnode* anode) { return AdjustRL(anode); } Tnode* AdjustLE_delete(Tnode* anode) //anode->balancefator == LH { Tnode* bnode = anode->lchild; anode->lchild = bnode->rchild; bnode->rchild = anode; anode->t_balancefacor = LH; bnode->t_balancefacor = RH; return bnode; } Tnode* AdjustRE_delete(Tnode* anode) //anode->balancefator == RH { Tnode* bnode = anode->rchild; anode->rchild = bnode->lchild; bnode->lchild = anode; anode->t_balancefacor = RH; bnode->t_balancefacor = LH; return bnode; } /******************************************************************************************* 1.删除 (1)Deleting a node with 0 children nodes 直接删除 (2)Deleting a node with 1 children nodes 将另一个子树接上 (3)Deleting a node with 2 children nodes : Replace the(to - delete) node with its in - order predecessor or in - order successor Then delete the in - order predecessor or in - order successor 2.调平衡 The actionPos (action position) in a delete operation is the parent node of the "deleted" node 不平衡点可能且只可能是被删除的节点的祖先节点 *********************************************************************************************/ bool deleteBBST(Tnode* &tree, float delete_data, bool& shorter) { shorter = false; bool deleted = false; if (tree == NULL) { return false; //删除失败 } else if(delete_data < tree->t_data) { deleted = deleteBBST(tree->lchild, delete_data, shorter); if (shorter) { switch (tree->t_balancefacor) //由原来祖先节点的balancefactor修改删除操作后现在祖先节点的balancefactor { case EQ: tree->t_balancefacor = RH; shorter = false; return deleted; case LH: tree->t_balancefacor = EQ; shorter = true; return deleted; case RH: //走到此分支均是因为删除节点之后,不平衡。此处进行rebalance,并重置balancefactor if ( tree->rchild->t_balancefacor == LH) { //delete 的 RL处理并修改平衡因子 tree = AdjustRL_delete(tree); shorter = true; return deleted; } else if (tree->rchild->t_balancefacor == RH) { //delete 的 RR处理并修改平衡因子 tree = AdjustRR_delete(tree); shorter = true; return deleted; } //比增加节点rebalance多此情况,是因为删除一个节点可能等效于使得另一分支增加了多个节点 else if (tree->rchild->t_balancefacor == EQ) { //delete 的 RE处理并修改平衡因子 tree = AdjustRE_delete(tree); shorter = false; return deleted; } } } } else if (delete_data > tree->t_data) { deleted = deleteBBST(tree->rchild, delete_data, shorter); if (shorter) { switch (tree->t_balancefacor)//由原来祖先节点的balancefactor修改删除操作后现在祖先节点的balancefactor { case EQ: tree->t_balancefacor = LH; shorter = false; return deleted; case RH: tree->t_balancefacor = EQ; shorter = true; return deleted; case LH: //走到此分支均是因为删除节点之后,不平衡。此处进行rebalance,并重置balancefactor if (tree->lchild->t_balancefacor == LH) { //delete 的 LL处理并修改平衡因子 tree = AdjustLL_delete(tree); shorter = true; return deleted; } else if (tree->lchild->t_balancefacor == RH) { //delete 的 LR处理并修改平衡因子 tree = AdjustLR_delete(tree); shorter = true; return deleted; } else if (tree->lchild->t_balancefacor == EQ) { //delete 的 LE处理并修改平衡因子 tree = AdjustLE_delete(tree); shorter = false; return deleted; } } } } else if (delete_data == tree->t_data) { //下面的前三个if为最简单操作,最后一种情况递归到上面if(shorter)里 // if(tree->lchild == tree->rchild == NULL) 会出现逻辑错误 if (tree->lchild == NULL && tree->rchild == NULL) { tree = NULL; shorter = true; deleted = true; return deleted; } else if ((tree->lchild != NULL) && (tree->rchild == NULL)) { tree = tree->lchild; shorter = true; deleted = true; return deleted; } else if ((tree->rchild != NULL) && (tree ->lchild == NULL)) { tree = tree->rchild; shorter = true; deleted = true; return deleted; } else //被删除的节点即存在左子树也存在右子树 { Tnode* inorder_predecessor = tree->lchild; //用来记录顺序前导节点 while (inorder_predecessor->rchild != NULL) { inorder_predecessor = inorder_predecessor->rchild; } tree->t_data = inorder_predecessor ->t_data; //替换要删除的节点 //原问题转化为在tree->lchild中删除inorder_predecessor指向的节点 deleted = deleteBBST(tree->lchild, inorder_predecessor->t_data, shorter); if (shorter) { switch (tree->t_balancefacor) //由原来祖先节点的balancefactor修改删除操作后现在祖先节点的balancefactor { case EQ: tree->t_balancefacor = RH; shorter = false; return deleted; case LH: tree->t_balancefacor = EQ; shorter = true; return deleted; case RH: //走到此分支均是因为删除节点之后,不平衡。此处进行rebalance,并重置balancefactor if (tree->rchild->t_balancefacor == LH) { //delete 的 RL处理并修改平衡因子 tree = AdjustRL_delete(tree); shorter = true; return deleted; } else if (tree->rchild->t_balancefacor == RH) { //delete 的 RR处理并修改平衡因子 tree = AdjustRR_delete(tree); shorter = true; return deleted; } //比增加节点rebalance多此情况,是因为删除一个节点可能等效于使得另一分支增加了多个节点 else if (tree->rchild->t_balancefacor == EQ) { //delete 的 RE处理并修改平衡因子 tree = AdjustRE_delete(tree); shorter = false; return deleted; } } } return deleted; } } }
参考:1
但请注意,这个资料里举例的图存在错误
相关文章推荐
- 平衡二叉树(AVL)--查找、删除、插入(Java实现)
- 平衡二叉树平衡二叉树(AVL)--查找、删除、插入(Java实现)
- 平衡二叉树(AVL)--查找、删除、插入(Java实现)
- 平衡二叉树的C语言实现(创建、插入、查找、删除、旋转)【数据结构】
- 平衡二叉树(AVL)的插入和删除详解(上)
- 平衡二叉树(AVL)的插入和删除详解(下)
- 平衡二叉树(AVL)的插入、删除、查找的java实现
- 线段树的创建插入查找删除
- 双向链表(创建、求长、打印、删除、插入)
- 笔试题:创建一个单链表,结点包含学生的学号,姓名,性别,年龄信息.写几个程序,实现按学生学号插入,查询,删除等操作.
- 堆的创建、插入、删除,模板参数实现堆以及模板的模板参数实现堆
- 平衡二叉树旋转,删除,插入
- list 基本操作 1 -- 创建,插入,删除,计算长度
- 链表的创建、取长、输出、插入、删除、逆序
- 单链表的创建、插入、删除、销毁以及查找中间结点
- 线性表创建插入删除及各种排序算法实现
- PostgreSQL连接python,postgresql在python 连接,创建表,创建表内容,插入操作,选择操作,更新操作,删除操作。
- (C语言版)链表(四)——实现双向循环链表创建、插入、删除、释放内存等简单操作
- 单链表的创建,表长,插入,查找,逆置,中间元素,删除节点,打印
- 用c++实现单向链表的创建,插入和删除