数据结构之AVL树
关于数据结构树的举例

关于数据结构树的举例
树是一种常见的数据结构,它有根节点、子节点和叶节点组成。
树的一个重要特点是它可以表达具有层次结构的数据。
以下是一些关于数据结构树的举例:
1. 二叉树:每个节点最多有两个子节点的树结构。
一种常见的应用是二叉搜索树,其中左子节点的值小于父节点的值,右子节点的值大于父节点的值。
2. AVL树:一种自平衡二叉搜索树,用于解决普通二叉搜索
树可能导致的不平衡问题。
AVL树通过旋转操作来使树保持
平衡。
3. 红黑树:另一种自平衡二叉搜索树,也用于解决二叉搜索树可能导致的不平衡问题。
红黑树通过颜色标记节点并进行旋转操作来保持平衡。
4. B树:一种用于在外部存储中组织大量数据的数据结构。
B
树具有多个子节点和键值对,并且在每个节点上有更多的子节点,以减少I/O操作。
5. 堆:一种特殊的树结构,用于快速访问最大或最小元素。
在大根堆中,父节点的值大于或等于子节点的值;在小根堆中,父节点的值小于或等于子节点的值。
6. 树状数组:一种特殊的树结构,用于高效地进行前缀和查询和更新操作。
树状数组通常用于解决区间求和等相关问题。
7. Trie树:一种用于存储和搜索字符串的数据结构。
Trie树逐
个字符存储字符串,并通过每个节点的子节点表示不同的字符。
这些只是数据结构树的一些常见例子,还有许多其他类型的树结构可用于各种应用。
二叉树的自平衡

二叉树的自平衡
自平衡二叉树是一种特殊的二叉查找树(Binary Search Tree,BST),它在插入或删除节点时能够自动调整树的结构,以保持树的平衡性。
平衡性的维护有助于确保在查找、插入和删除等操作时,树的性能保持在较高水平。
常见的自平衡二叉树包括:
1.A VL树:A VL树是一种最早被发明的自平衡二叉树。
在A VL树中,任意节点的左右子树高度之差(平衡因子)不能超过1。
当进行插入或删除操作后,如果破坏了平衡性,A VL树会通过旋转操作(左旋或右旋)来重新平衡。
2.红黑树:红黑树是一种更为灵活的自平衡二叉树。
在红黑树中,每个节点都被标记为红色或黑色,并通过一些规则确保树的平衡性。
这些规则包括节点颜色的变换和树的旋转。
3.Splay树:Splay树在每次访问一个节点后,将该节点移动到树的根位置,以提高后续对该节点的访问速度。
Splay树不维持固定的平衡条件,但通过频繁的局部调整来实现整体的平衡。
4.Treap(树堆):Treap是一种随机化的自平衡二叉树,结合了二叉搜索树和堆的性质。
每个节点有一个随机的优先级值,通过调整节点的优先级和执行旋转来保持树的平衡。
这些自平衡二叉树的设计灵感各异,选择适当的树取决于应用的具体要求。
自平衡二叉树的主要优势是保持较低的查找、插入和删除操作的时间复杂度,使其在很多应用中都是一个有用的数据结构。
各种二叉树的介绍

各种二叉树的介绍
二叉树是一种常见的数据结构,每个节点最多只能有两个子节点,通常称为左子节点和右子节点。
根据二叉树的不同特性和限制,可以将其分为多种类型,包括普通二叉树、满二叉树、完全二叉树、平衡二叉树等。
普通二叉树:这是最基本的二叉树形式,每个节点最多有两个子节点,且没有特定的限制条件。
满二叉树:在满二叉树中,所有叶子节点都在最后一层,且节点总数为2^n-1,其中n为层数。
也就是说,除了叶子节点外,每个节点都有两个子节点。
完全二叉树:完全二叉树的所有叶子节点都在最后一层或倒数第二层,且最后一层的叶子节点在左边连续,倒数第二层的叶子节点在右边连续。
如果将满二叉树从右至左、从下往上删除一些节点,剩余的结构就构成完全二叉树。
平衡二叉树(AVL树):平衡二叉树是一种特殊的二叉树,它要求每个节点的左子树和右子树的高度差绝对值不超过1,且每个子树也必须是一棵平衡二叉树。
这种树的查找效率通常高于普通二叉树,因此常用于需要频繁查找的场景。
此外,还有一些特殊的二叉树,如红黑树、B树、B+树等,它们具有不同的特性和应用场景。
红黑树是一种自平衡的二叉查找树,它的左右子树高度差有可能大于1,但通过对节点进行旋转和重新着色等操作,可以保持树的平衡性。
B树和B+树则常用于数据库和文件系统中,它们支持对节点进行分裂和合并操作,以满足快速查找、插入和删除数据的需求。
总之,二叉树是一种非常有用的数据结构,它可以用于实现各种算法和应用,如排序、搜索、压缩、加密等。
不同类型的二叉树具有不同的特性和应用场景,需要根据具体需求进行选择和使用。
数据结构之二叉树(BinaryTree)

数据结构之⼆叉树(BinaryTree)⽬录导读 ⼆叉树是⼀种很常见的数据结构,但要注意的是,⼆叉树并不是树的特殊情况,⼆叉树与树是两种不⼀样的数据结构。
⽬录 ⼀、⼆叉树的定义 ⼆、⼆叉树为何不是特殊的树 三、⼆叉树的五种基本形态 四、⼆叉树相关术语 五、⼆叉树的主要性质(6个) 六、⼆叉树的存储结构(2种) 七、⼆叉树的遍历算法(4种) ⼋、⼆叉树的基本应⽤:⼆叉排序树、平衡⼆叉树、赫夫曼树及赫夫曼编码⼀、⼆叉树的定义 如果你知道树的定义(有限个结点组成的具有层次关系的集合),那么就很好理解⼆叉树了。
定义:⼆叉树是n(n≥0)个结点的有限集,⼆叉树是每个结点最多有两个⼦树的树结构,它由⼀个根结点及左⼦树和右⼦树组成。
(这⾥的左⼦树和右⼦树也是⼆叉树)。
值得注意的是,⼆叉树和“度⾄多为2的有序树”⼏乎⼀样,但,⼆叉树不是树的特殊情形。
具体分析如下⼆、⼆叉树为何不是特殊的树 1、⼆叉树与⽆序树不同 ⼆叉树的⼦树有左右之分,不能颠倒。
⽆序树的⼦树⽆左右之分。
2、⼆叉树与有序树也不同(关键) 当有序树有两个⼦树时,确实可以看做⼀颗⼆叉树,但当只有⼀个⼦树时,就没有了左右之分,如图所⽰:三、⼆叉树的五种基本状态四、⼆叉树相关术语是满⼆叉树;⽽国际定义为,不存在度为1的结点,即结点的度要么为2要么为0,这样的⼆叉树就称为满⼆叉树。
这两种概念完全不同,既然在国内,我们就默认第⼀种定义就好)。
完全⼆叉树:如果将⼀颗深度为K的⼆叉树按从上到下、从左到右的顺序进⾏编号,如果各结点的编号与深度为K的满⼆叉树相同位置的编号完全对应,那么这就是⼀颗完全⼆叉树。
如图所⽰:五、⼆叉树的主要性质 ⼆叉树的性质是基于它的结构⽽得来的,这些性质不必死记,使⽤到再查询或者⾃⼰根据⼆叉树结构进⾏推理即可。
性质1:⾮空⼆叉树的叶⼦结点数等于双分⽀结点数加1。
证明:设⼆叉树的叶⼦结点数为X,单分⽀结点数为Y,双分⽀结点数为Z。
平衡二叉树

编辑
红黑树
红黑树是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它是在1972年由Rudolf Bayer发明的,他称之为"对称二叉B树",它现代的名字是在 Leo J. Guibas 和 Robert Sedgewick 于1978年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n是树中元素的数目。
伸展树
伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。它由Daniel Sleator和Robert Tarjan创造。它的优势在于不需要记录用于平衡树的冗余信息。在伸展树上的一般操作都基于伸展操作。
SBT
Size Balanced Tree(简称SBT)是一自平衡二叉查找树,是在计算机科学中用到的一种数据结构。它是由中国广东中山纪念中学的陈启峰发明的。陈启峰于2006年底完成论文《Size Balanced Tree》,并在2007年的全国青少年信息学奥林匹克竞赛冬令营中发表。由于SBT的拼写很容易找到中文谐音,它常被中国的信息学竞赛选手和ACM/ICPC选手们戏称为“傻B树”、“Super BT”等。相比红黑树、AVL树等自平衡二叉查找树,SBT更易于实现。据陈启峰在论文中称,SBT是“目前为止速度最快的高级二叉搜索树”。SBT能在O(log n)的时间内完成所有二叉搜索树(BST)的相关操作,而与普通二叉搜索树相比,SBT仅仅加入了简洁的核心操作Maintain。由于SBT赖以保持平衡的是size域而不是其他“无用”的域,它可以很方便地实现动态顺序统计中的select和rank操作。
AVL树与B树的比较数据结构中的平衡树对比

AVL树与B树的比较数据结构中的平衡树对比在数据结构中,平衡树是一种常见的数据结构,用于在插入和删除操作时保持树的平衡,以确保检索效率。
AVL树和B树都是常见的平衡树结构,它们在不同场景下有着各自的优势和特点。
本文将对AVL 树和B树进行比较,探讨它们在数据结构中的应用和区别。
### AVL树AVL树是一种自平衡二叉搜索树,它的特点是任意节点的左右子树高度差不超过1。
当在AVL树中进行插入或删除操作时,系统会通过旋转操作来保持树的平衡。
AVL树的平衡性能较好,适用于对读操作较多的场景。
#### 优点1. **平衡性好**:AVL树能够保持树的平衡,确保检索效率稳定。
2. **适用于静态数据集**:适合对静态数据集进行频繁的搜索操作。
#### 缺点1. **频繁的旋转操作**:在插入和删除操作时,可能需要频繁进行旋转操作,影响性能。
2. **空间需求较大**:由于需要存储额外的平衡因子,占用的空间较大。
### B树B树是一种多路搜索树,常用于文件系统和数据库中。
B树的特点是每个节点可以包含多个子节点,节点中的关键字按顺序排列。
B树的平衡性是通过调整节点的大小和结构来实现的,适用于对写操作较多的场景。
#### 优点1. **适用于磁盘存储**:B树适合在磁盘存储中进行数据检索,减少磁盘I/O次数。
2. **写操作效率高**:B树的平衡性能较好,适合对数据频繁进行插入和删除操作。
#### 缺点1. **平衡性相对较差**:相比AVL树,B树的平衡性能略逊一筹。
2. **节点结构复杂**:B树的节点结构较为复杂,实现和维护相对困难。
### AVL树与B树的比较1. **平衡性能**:AVL树的平衡性能优于B树,适合对静态数据集进行频繁的搜索操作;而B树适合对写操作较多的场景,能够减少磁盘I/O次数。
2. **空间需求**:AVL树由于需要存储额外的平衡因子,空间需求较大;而B树的节点结构较为复杂,实现和维护相对困难。
2024年度如何学习AVL

文件系统
在文件系统中,目录结构通常采用类似AVL树的平衡树结构,以便快速定位文件或目录。这种结构可以保持文件 系统的平衡,提高文件访问速度。
2024/3/23
18
数据动态变化且需保持有序场景
实时数据处理
在实时数据处理系统中,数据会不断动态变化。使用AVL树可以确保数据在插 入、删除等操作后仍然保持有序,便于进行实时分析和处理。
LR旋转(先左旋 后右旋)
RL旋转(先右旋 后左旋)
当在左子树的右子树中 插入新节点导致平衡因 子失衡时,进行LR旋转 。具体操作为先将失衡 节点的左子树进行左旋 操作,再将失衡节点进 行右旋操作。
当在右子树的左子树中 插入新节点导致平衡因 子失衡时,进行RL旋转 。具体操作为先将失衡 节点的右子树进行右旋 操作,再将失衡节点进 行左旋操作。
初始化AVL树
将根节点指针指向空节点 ,表示AVL树为空。
12
插入、删除操作代码实现
插入操作
从根节点开始,按照二叉搜索树的规则找到插入 位置。
更新插入路径上所有节点的高度。
2024/3/23
13
插入、删除操作代码实现
• 检查是否需要进行平衡调整,若需要则进 行相应的旋转操作。
2024/3/23
14
01
《算法导论》(Introduction to Algorithms):这本经典教材 深入浅出地介绍了各种算法和数据结构,包括AVL树。它既有严 谨的理论分析,也提供了大量的实例和习题,是学习AVL的必备 参考书。
2024/3/23
02 03
《数据结构与算法分析》(Data Structures and Algorithm Analysis in Java):这本书详细讲解了如何 使用Java实现各种数据结构和算法,其中也包括AVL树。 它通过实例和图表帮助读者更好地理解AVL树的原理和实 现。
avl的引入流程

avl的引入流程AVL树是一种自平衡二叉树,它通过旋转操作来保持树的平衡,确保任何节点的左子树和右子树的高度差最多为1、在引入AVL树的过程中,需要进行如下几个步骤:1.引入背景:首先,我们需要说明引入AVL树的背景和初衷。
AVL树的概念最早由G.M. Adelson-Velsky和E.M. Landis在1962年提出,他们希望通过这种数据结构来提高和插入操作的平均和最糟糕情况下的时间复杂度。
2.定义:接下来,我们需要定义AVL树的性质和操作。
AVL树是一种二叉树,它具有以下几个特点:-每个节点最多有两个子节点。
-左子树和右子树的高度差最多为1-每个节点的值大于其左子树中所有节点的值,小于其右子树中所有节点的值。
3.插入操作:在向AVL树中插入新节点时,需要进行一系列的旋转操作来保持树的平衡。
插入操作的过程如下:-首先,我们需要将新节点插入到合适的位置,使得树仍然保持二叉树的性质。
-然后,我们从插入点开始向上遍历,检查每个祖先节点是否平衡。
如果祖先节点不平衡,需要进行旋转操作来调整平衡。
-旋转操作分为两种类型:左旋和右旋。
左旋可以平衡右子树比左子树高的情况,右旋可以平衡左子树比右子树高的情况。
通过旋转操作,我们可以将不平衡的子树转换为平衡的子树。
-当所有祖先节点都平衡时,整个插入操作完成。
4.删除操作:在从AVL树中删除节点时,也需要进行一系列的旋转操作来保持树的平衡。
删除操作的过程如下:-首先,我们需要找到要删除的节点。
如果节点不存在,则删除操作完成。
-如果要删除的节点有两个子节点,可以选择用其前驱或后继节点替换它,并将要删除的节点变为其前驱或后继节点。
-删除节点后,我们需要从删除点开始向上遍历,检查每个祖先节点是否平衡。
如果祖先节点不平衡,需要进行旋转操作来调整平衡。
-同样,旋转操作分为左旋和右旋,以调整平衡。
-当所有祖先节点都平衡时,整个删除操作完成。
5.性能分析:最后,我们需要对AVL树的性能进行分析。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
左边为调整前得节点,我们可以看出k1的左右子树已不再满足AVL平衡 条件,调整后的为右图。 我们可以看出,解决办法是将z上移一层,并将x下移一层,由于在原树 中k2 > k1,所以k1成为k2的左子树,而y是大于k1的,所以成为k1的右 子树。 为了设计算法,我们这里来看一个更易理解的:插入的是节点“6”
1.3
插入操作
插入的核心思路是通过递归找到合适的位置,插入新结点,然后看新结
点是否平衡(平衡因子是否为2),如果不平衡的话,就分成三种大情 况以及两种小情况: 1. 在结点的左儿子(X < T->item) 在左儿子的左子树 (X < T->l-> item),“外边”,要 做单旋转。 在左儿子的右子树 (X > T->l-> item),“内部”,要 做双旋转。 2. 在结点的右儿子(X > T->item) 在右儿子的左子树(X < T->r-> item),“内部”,要 做双旋转。 在右儿子的右子树(X > T->r-> item),“外边”,要 做单旋转。 3. (X == T->item) ,对该节点的计数进行更新。 当进行了旋转之后,必定会有结点的“父结点”是需要更新的,例如: 2 / \ 1 4 / \ 3 5 \ 6 上图是调整前的,下图是调整后的: 4 / \ 2 5 / \ \ 1 3 6 可以看出,根结点2不平衡,是由于它的右儿子的右子树插入了新的结 点6造成的。因此,这属于“外边”的情况,要进行一次单旋转。于是 我们就把结点4调整上来作为根结点,再把结点2作为4的左儿子,最后 把结点2的右儿子修改为原来的结点4的左儿子。 实现代码: AVLTree Insert(Item X, AVLTree T ) { if( T == NULL ) { /* Create and return a one-node tree */
1.3.2
双旋转
情况2:对该结点的左儿子的右子树进行了一次插入。 这种情况是单旋转调整不回来的,如下图:
图(1) 左--右双旋转如下:
图(2) 这里我们将图(1)的Y子树看成如图(2),以k2为子树根节点的树, 我们将其子树分成比D低,这里我我先对k3的左子树进行一次情形四的
右旋转,然后在进行一次情形1的左旋转,详细步骤如下:(红色框里面 的即是要进行单旋转的) 实现代码: //情形2 AVLTree DoubleRotateWithLeft( PAVLNode k3 ) { /* Rotate between K1 and K2 */ k3->l = SingleRotateWithRight( k3->l ); /* Rotate between K3 and K2 */ return SingleRotateWithLeft(k3); } 情况3:对该结点的右儿子的左子树进行了一次插入。 右—左双旋转如下:
T = (PAVLNode)malloc( sizeof(AVLNode ) ); if( T == NULL ) perror("malloc failed"); else { T->item = X; T->h = 0; T->l = T->r = NULL; T->count = 1; } } else if(compare(&X,&T->item) == -1)//插入情况1 { T->l = Insert( X, T->l ); if( Height( T->l ) - Height( T->r ) == 2 ) if(compare(&X, &T->l->item ) == -1)//左边左子树 单旋转 T = SingleRotateWithLeft( T ); else T = DoubleRotateWithLeft( T );//左边右子树 } else if( compare(&X,&T->item) == 1 ) //插入情况2 { T->r = Insert( X, T->r ); if( Height( T->r ) - Height( T->l ) == 2 ) if(compare(&X , &T->r->item) == 1)//右边右子树 单旋转 T = SingleRotateWithRight( T ); else T = DoubleRotateWithRight( T );//右边左子树 } else//插入情况3 T->count++; /* Else X is in the tree already; we'll do nothing */ T->h = MAX( Height( T->l ), Height( T->r ) ) + 1; return T; }
k1->r = k2; k2->h = MAX( Height( k2->l ), Height( k2->r ) ) + 1; k1->h = MAX( Height( k1->l ), k2->h ) + 1; return k1; /* New root */ } 情况4:对该结点的右儿子的右子树进行了一次插入。
1.2
为什么要用AVL树?
有人也许要问:为什么要有AVL树呢?它有什么作用呢? 我们先来看看二叉搜索树吧(因为AVL树本质上是一棵二叉搜索树), 假设有这么一种极端的情况:二叉搜索树的结点为1、2、3、4、5,也 就是: 1 \ 2 \ 3 \ 4 \ 5
聪明的你是不是发现什么了呢?呵呵,显而易见——这棵二叉搜索树其 实等同于一个链表了,也就是说,它在查找上的优势已经全无了——在 这种情况下,查找一个结点的时间复杂度是O(N)! 好,那么假如是AVL树(别忘了AVL树还是二叉搜索树),则会是: 2 / \ 1 4 / \ 3 5 可以看出,AVL树的查找平均时间复杂度要比二叉搜索树低——它是 O(logN)。也就是说,在大量的随机数据中AVL树的表现要好得多。
我们先对k1的右子树进行一次左旋转(情形1,然后再对k1进行一次右 旋转(情形4)。 实现代码: //情形3 AVLTree DoubleRotateWithRight( PAVLNode k1 ) { /* Rotate between K3 and K2 */ k1->r = SingleRotateWithLeft( k1->r ); /* Rotate between K1 and K2 */ return SingleRotateWithRight( k1 ); }
算法设计:由于是情形1对该结点的右儿子的右子树进行了一次插入, 该节点为“4”,我们同第一种情形类似。 实现代码: //情形4 AVLTree SingleRotateWithRight(PAVLNode k1) {
PAVLNode k2; k2 = k1->r; k1->r = k2->l; k2->l = k1; k1->h = MAX( Height( k1->l ), Height( k1->r ) ) + 1; k2->h = MAX( Height( k2->r ), k1->h ) + 1; return k2; /* New root */ }
算法设计:由于是情形1对该结点的左儿子的左子树进行了一次插入, 该节点是“8”,首先我们不考虑其父节点的情况,因为我们创建节点是 递归创建的,可以不用考虑其父节点与其的连接,这在后面递归创建的 时候会说到,由于“8”的右孩子将不会发生变化,但是其左孩子设 为“7”的右孩子,将7的右孩子设为“8”及其子树,然后返回“7”节点的指 针。 实现代码: //情形1 AVLTree SingleRotateWithLeft(PAVLNode k2) { PAVLNode k1; k1 = k2->l; k2->l = k1->r;
1.3
旋转
假设有一个结点的平衡因子为2(在AVL树中,最大就是2,因为结点是 一个一个地插入到树中的,一旦出现不平衡的状态就会立即进行调整, 因此平衡因子最大不可能超过2),那么就需要进行调整。由于任意一 个结点最多只有两个儿子,所以当高度不平衡时,只可能是以下四种情 况造成的: 1. 对该结点的左儿子的左子树进行了一次插入。 2. 对该结点的左儿子的右子树进行了一次插入。 3. 对该结点的右儿子的左子树进行了一次插入。 4. 对该结点的右儿子的右子树进行了一次插入。 情况1和4是关于该点的镜像对称,同样,情况2和3也是一对镜像对称。 因此,理论上只有两种情况,当然了,从编程的角度来看还是四种情 况。 第一种情况是插入发生在“外边”的情况(即左-左的情况或右-右的情 况),该情况可以通过对树的一次单旋转来完成调整。第二种情况是插 入发生在“内部”的情况(即左-右的情况或右-左的情况),该情况要 通过稍微复杂些的念
AVL树的复杂程度真是比二叉搜索树高了整整一个数量级——它的原理 并不难弄懂,但要把它用代码实现出来还真的有点费脑筋。下面我们来 看看:
1.1
AVL树是什么?
AVL树本质上还是一棵二叉搜索树(因此读者可以看到我后面的代码是 继承自二叉搜索树的),它的特点是: 1. 本身首先是一棵二叉搜索树。 2. 带有平衡条件:每个结点的左右子树的高度之差的绝对值 (平衡因子)最多为1。 例如: 5 5 / \ / \ 2 6 2 6 / \ \ / \ 1 4 7 1 4 / / 3 3 上图中,左边的是AVL树,而右边的不是。因为左边的树的每个结点的 左右子树的高度之差的绝对值都最多为1,而右边的树由于结点6没有子 树,导致根结点5的平衡因子为2。
1.3.1
单旋转