avl

红黑树和AVL树(平衡二叉树)的定义、特点以及两者的区别

匿名 (未验证) 提交于 2019-12-03 00:05:01
红黑树和AVL树(平衡二叉树)的定义、特点以及两者的区别 定义 性质 区别 定义 AVL树:平衡二叉树又称AVL树,是一种特殊的二叉查找树,其左右子数都是平衡二叉树,且左右子树高度差的绝对值不超过1.一句话表述为:以树中所有结点为根的树的左右子树高度差的绝对值不超过1.将二叉树上结点的左子树深度减去右子树深度称为平衡因子BF,那么平衡二叉树上的所有结点的平衡因子只可能是-1、0和1.只要二叉树上有一个结点的平衡因子的绝对值大于1,该二叉树就是不平衡的。 红黑树:是一种二叉查找树,但在每个结点增加一个存储位表示结点的颜色,可以是红或者黑(非黑即红)。通过对任何一条从根到叶子的路径上各个结点着色的方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因此红黑树是一种弱平衡二叉树,相对于要求严格的AVL树来说,它的旋转次数少,所以对于搜索、插入、删除操作比较多的情况下,通常使用红黑树。 性质 红黑树的性质如下:1.每个结点非红即黑;2.根节点是黑的;3.每个叶节点(叶节点即树尾端NULL指针或NULL结点)都是黑的;4.如果一个结点是红色的,则它的子节点必须是黑色的;5.对于任何结点而言,其到叶子点树NULL指针的每条路径都包含相同数目的黑结点。 区别 区别:AVL树是高度平衡的,频繁的插入和删除,会引起频繁的rebalance,导致效率下降;红黑树不是高度平衡的,算是一种折中

AVL树的插入、删除、查找操作

雨燕双飞 提交于 2019-11-29 21:16:36
AVL树 是一种带有平衡条件的二叉查找树,所有节点都是基于平衡的。所谓平衡,即某个节点的左右子树的高度差最多相差1。空树的高度定义为-1。 下面我们通过如下两棵树(图片来自《数据结构与算法分析–C语言描述》)进一步了解怎样的树才是AVL树。 上图只有左边的树才是AVL树,那么下面我们来分析一下左边的树 (某个节点的高 = MAX(子树的高, 右子树的高 )+ 1) 节点3的高为0 节点4的高为1 节点1的高为0 节点7的高为0 节点2的高为2 节点8的高为1 节点5的高为3 你可以看到任一节点的左右子树的差都没有超过2,那么我们再看看右边的树 节点2的高为2 节点8的高为0 所以对于节点7来说,左右子树的高度差相差为2 > 1,失去平衡。 下面我们接着来说AVL的插入、删除、查找操作。 插入和删除操作都有可能导致AVL树的不平衡 (为什么呢?原来的树是平衡的,即某个节点的左右子树的高度差小于2,假设左子树的高为1,右子树的高为0,如果这个时候,在这个节点的左子树插入一个节点,那么该节点的左右子树的高度差就为2了,删除操作也是一样的)。 对于出现不平衡的时候,需要通过 旋转 来解决。下面我们直接来看看插入操作。 插入操作 插入节点==》有可能失去平衡==》通过旋转操作来使树达到平衡 我们把需要重新平衡的节点叫做A,那么出现不平衡的情况,会有下面四种 : 1.

AVL平衡树插入删除结点过程平衡操作图示

笑着哭i 提交于 2019-11-29 21:12:29
AVL插入结点 通过这张图来描述AVL平衡树在插入新结点过程中,通过旋转操作来达到自平衡的四种场景: LL单旋转:新结点插入在A的左孩子(L)的左子树(L),这种场景在插入新结点后,同一路径上的A和B的平衡因子符号相同(2,1),只需要一次右旋操作即可重新达到平衡。 LR双旋转:新结点插入在A的左孩子(L)的右子树(R),这种场景在插入新结点后,同一路径上的A和B的平衡因子符号不同(2,-1),先对以B为根的二叉树左旋一次,再对以A为根的二叉树右旋一次即可重新达到平衡。 RL双旋转:新结点插入在A的右孩子(R)的左子树(L),这种场景在插入新结点后,同一路径上的A和B的平衡因子符号不同(-2,1),先对以B为根的二叉树左旋一次,再对以A为根的二叉树右旋一次即可重新达到平衡。 RR单旋转:新结点插入在A的右孩子(R)的右子树(R),这种场景在插入新结点后,同一路径上的A和B的平衡因子符号相同(-2,-1),对以A为根的二叉树右旋一次即可重新达到平衡。 所以,实际上只有左旋和右旋两种最基本的操作,只是每次旋转的二叉树不一样。对一棵二叉树进行旋转,实际上就是让其根结点的左孩子(右旋)或者右孩子(左旋)来代替原根结点的位置,并保证整棵树中序遍历的顺序不变。 注解: 其中的A结点是插入新结点的祖先,在插入新结点前,以这个结点为根结点的二叉树的平衡因子一定是1或者-1。

看数据结构写代码(57) AVL树的删除

北慕城南 提交于 2019-11-29 21:11:38
上一节 已经说了 AVL树的插入 操作,可是 只有 插入,没有删除,怎么能叫 动态 查找表呢。 呵呵,博主 赶紧 去 研究了一番。下面 是成果: AVL树的删除 大致 分为 两大块: 1. 查找节点 并 删除 2. 保持 删除 后 平衡因子的 影响 1. 首先 找到 这个 节点,如果 节点 不存在,直接 退出 函数 if (*tree == NULL){//没找到 return false; } 2.如果 存在,分为 四种情况:(根 二叉 排序树的 删除 类似) 1.节点 为 叶子 节点,直接 删除 2.节点 的 左子树为空,则 用 节点的 右子树 代替 节点,并删除 这个节点。 3.节点的 右子树为空,则用 节点的 左子树 代替节点,并 删除 这个 节点 4.左右子树 都不为空 (下面 这样做,是为了 减少 旋转的 次数 ,如果 不懂,请 往下看,看完,再回头看) 4.1 如果 节点的 左子树的高度 >= 右子树的高度(LH,EH),则 从 左子树里 寻找 值 最大节点 , 将最大值赋值 给 节点,并删除 最大节点 。 4.2如果节点 的 左子树的高度 《 右子树的高度(RH), 则从 右子树里 寻找 值 最小的节点,并将 最小值 赋值给 节点,并删除最小节点 if (data == key){ if (p->lChild == NULL){//叶子节点 或者 左孩子为空

AVL树

白昼怎懂夜的黑 提交于 2019-11-29 21:11:17
AVL树这样一棵搜索二叉树,它的左右子树的深度之差不超过1。因此,他是带有条件的搜索二叉树。这个条件保证了AVL树的深度是O(log n).最简单的想法是左右两棵子树保持相同的高度。但是这种条件过于苛刻,难以使用。AVL只要求深度之差不超过1。AVL解决了二叉搜索树带来的不平衡问题。但是要求变成了我们必须在每次操作后进行调整,以使得AVL树保持平衡。另一种较新的方法是放弃平衡条件,允许树有任意的深度,但是在每次操作后要进行调整,以使得后面的操作效率更高。有一种这样的树称之为伸展树。 在AVL树的每一个节点中保留其高度信息是必须的。在一棵高度为h的AVL树中,最少节点数S(h) = S(h-1)+S(h-2)+1。对于h为0时,S(h)=1;h为2时,S(h)=2。这个函数与斐波那契数列密切相关。 如果一个插入操作破坏了AVL树的平衡条件,那么这插入后形成的左右子树的高度之差最大是2。因此只会出现4种情形。 对节点的左儿子的左子树进行一次插入; 对节点的右儿子的右子树进行一次插入; 对节点的左儿子的右子树进行一次插入; 对节点的右儿子的左子树进行一次插入; 上述4种情形之中,1和2是镜像对称的,3和4是镜像对称的。所以看起来只有两种情况,但是对于编程实现而言还是4种情形。对于1,2这样的插入操作,可以通过单旋转来完成;对于3,4这样的插入操作,需要通过稍微复杂的双旋转操作来完成。

AVL树详解与总结

那年仲夏 提交于 2019-11-29 21:10:49
前言: 什么叫做AVL树? AVL树的定义: 1、AVL的左右子树高度之差的绝对值不超过1; 2、树中的左右子树都为AVL树 3、平衡因子只能是(-1、0、1) AVL树的效率 AVL树的总共节点为N个,他的高度能搞保持在logN,插入、删除、查找等操作的时间复杂度也是logN。 AVL树的实现: 1、AVL树的插入: 思路分析:1> 既然是插入就需要先找到插入的位置,使用while循环遍历找到插入位置 2> 找到插入位置以后直接插入,然后向上遍历修改父节点的平衡因子,当修改完以后的平衡因子有等于2或者-2的挑出来,进行树的旋转,并且调整平衡因子,使满足AVL树的定义。 3> 旋转过程中需要考虑是左旋转、右旋转、左右旋转、还是右左旋转。左右旋转简单,直接调用自己编写的左右旋转函数即可,只要传过来的节点记得加上引用就行了,这样能够直接连接到旋转后的子树。重点在于左右和右左旋转,我们是否能直接调用左旋转函数和右旋转函数各一次?答案是否定的,因为在旋转的过程中,不是在插入的那个位置进行旋转,而是在中间进行旋转,这是后父亲节点和祖父节点的平衡因子BF就需要自己手动来定义了。以右左旋转来分析: 如果当前节点的平衡因子为-1,那么他的parent应该为1,pparent应该为0; 如果当前节点的平衡因子为1,那么他的parent应该为0,pparent应该为-1; 如果当前节点的平衡因子为0

AVL树的插入与删除(详解)

佐手、 提交于 2019-11-29 21:10:27
AVL树的插入与删除(详解) 平衡二叉树的定义就不在这里赘述了,平衡二叉树的插入与删除都是基于平衡二叉树的查找进行的。平衡二叉树的查找和二叉树的查找又是一样的。 插入的话,我们从平衡二叉树的根结点出发查找我们需要插入的元素,如果查找到了表明该元素已经存在树中,因此就不用插入了,若没有查找到该元素就要创建这个元素结点,将结点插入我们所访问的那个查找失败的结点位置(查找失败结点是我们虚构的一个结点)。 删除的话,我们也是从根结点出发,找到我们需要删除元素的结点。若找到就将该结点删除。 前面的道理很简单,难就难在插入元素结点之后,我们的树可能会失去平衡。要学懂平衡二叉树,重点就在掌握插入和删除结点之后对树的平衡维护。树的平衡维护是通过平衡旋转来实现的。插入和删除结点导致的不平衡具体的旋转方法是不一样的。书上把结点的插入讲的很详细,但是结点的删除。。。我找了很多书,书上都没讲,刚开始我以为删除和插入的旋转是一样的打了好几大篇的草稿,各种蛇皮旋转,最终发现它和删除是不一样的(博主我已接近脑死亡)。最后又在网上找别人写的博客看,能把删除讲明白的很少,能用代码实现的反正是我没找到,好多都有问题(C语言实现的我不清楚,因为我C学的不太好)。但这至少个过程还是有收获的,有了一些思路,解决了一些我刚开始遇到的某些删除旋转问题。不懈努力之后,最终将删除的所有情况自己总结了出来,后面我会一一详细的讲解

AVL树删除

前提是你 提交于 2019-11-29 21:10:07
AVL Tree与BST 由于AVL树本身就是一颗自平衡的BST,因此其的删除比起BST仅仅多了删除后平衡的调整 查找: AVL树中一定不存在两个值大小相同的节点 从AVL树根开始向下搜索,根据意图删除值与当前节点值大小关系进行下一步操作: 1、 当前节点值大于意图删除值,保存当前节点并在当前节点的左子树中继续查找意图删除值 2、 当前节点值小于意图删除值,保存当前节点并在当前节点的右子树中继续查找意图删除值 3、 当前节点值等于意图删除值,准备进行删除 查找的过程中保存当前节点,是为了便于回溯并修改、判断可能被删除影响的节点的平衡因子 删除: 根据意图删除节点的子树个数可以将删除分为三种情况: 1、 意图删除的节点没有子树(叶子节点) 2、 意图删除的节点有且仅有一个子树 3、 意图删除的节点既有左子树又有右子树 与BST的删除相同,这三种情况又可以概括为两种情况: 1、 意图删除节点最多存在一个子树 2、 意图删除节点存在两个子树 情况1的删除非常简单,就不解释了,情况2下需要将删除有两个子树的节点转换为删除仅有一个子树的节点: 使用意图删除节点的最大左子树值或者最小右子树值替换意图删除值,再删除替换值所在节点 删除完成后立即根据之前查找时保存的节点信息回溯(修改平衡因子并立即判断平衡因子) 调整平衡: AVL树是否平衡是由平衡因子决定的,修改完平衡因子后应立即判断平衡因子

AVL树的插入与删除操作

坚强是说给别人听的谎言 提交于 2019-11-29 21:09:43
AVL树是一种较老的数据结构,它的出现是为了解决了二叉查找树在最坏情况下的插入结果。 二叉查找树(以下称为二叉树)如果能以较好的插入序列来创建,使得树的结构趋于平衡,则其大部分操作都可以O(logN)的复杂度实现。但如果以类似{1,2,3,4,5,6,.....}这种已序序列来进行插入,那么形成的二叉树将是极不平衡的,甚至有可能只有左子树或只有右子树,这样的二叉树与链表是没有太大区别的,各种操作的复杂度也将升至O(N)这种无法接受的线性复杂度。 如果可以为二叉树设置一种限定条件,使得对于每个根节点,其左子树与右子树的高度差在一个可以接受的范围内(实现中为1),那么二叉树的平衡性就可以保证,以上就是AVL树的原始的设计理念。 由此引发了两个问题:1.我们怎样才能够随时获取每个节点的高度来作为是否平衡的信息呢? 2. 我们如何调整树的结构(实际上是对指针的调整)来使得树从不平衡变为平衡? 问题1有两种解决方案,一种是设计一个函数,其接受一个节点参数,返回此节点的高度。这种函数式的思想是自然而然的,由于此函数可以使用递归实现,其编程复杂度也不高。但是,如果树的高度足够高,在同一时间,我们为了一个简单信息,有可能在在同一时间内,造成大量函数栈在内存中堆叠。越是庞大的二叉树,这种方式的成本就越高,有点得不偿失。 另一种解决方案是,为每一个节点,保存其高度信息,并在插入

AVL树的插入与删除

隐身守侯 提交于 2019-11-29 21:05:05
AVL树是一种高度平衡的二叉搜索树,其每一个结点的左树高和右树高相差不大于1。这个性质使得AVL树的搜索效率要比普通的二叉搜索树要高,因为对于一组递增的数组,其构成的二叉搜索树会是一个链表,搜索时间复杂度自然就是O(n),而其构成的AVL树则肯定是一个搜索效率为O(lg(n))的二叉树。也正因为此,为了保持其平衡的性质,AVL树的插入和删除要比普通二叉搜索树要复杂。 二叉搜索树可以参考 二叉搜索树的插入与删除 1. 通过旋转保持AVL树的平衡 旋转分为左旋和右旋两种,这两种操作是对称的,如下图所示。 在插入或者删除结点后,AVL树保持平衡的方法是旋转。对于下图中一颗左子树要比右子树高大1的树来说,如果在其左子树中插入一个结点,则会引起该树不平衡。此时,可通过一次或两次旋转使树平衡。 当结点被插入到b结点的左子树下,只需要对树a进行一次右旋即可,如下图所示,旋转后的树a左子树和右子树高会达到平衡。 当结点被插入到b结点的右子树下,则需要先对树b进行一次左旋转变成上一种情况,再对树a进行一次右旋,如下图所示。 对树b进行左旋后,树b的左子树比右子树高1,即转变为上一种情况(将结点插入到树b的左子树中),然后再对树a进行一次右旋即可将树调整回平衡状态。为什么要对树b进行一次左旋呢?假设我们部队树b左旋,直接对树a右旋,得到的树会是如下图所示。 旋转后的树并没有变得平衡!原因很简单