数据结构第三部分.ppt

上传人:sccc 文档编号:5354739 上传时间:2023-06-28 格式:PPT 页数:440 大小:2.24MB
返回 下载 相关 举报
数据结构第三部分.ppt_第1页
第1页 / 共440页
数据结构第三部分.ppt_第2页
第2页 / 共440页
数据结构第三部分.ppt_第3页
第3页 / 共440页
数据结构第三部分.ppt_第4页
第4页 / 共440页
数据结构第三部分.ppt_第5页
第5页 / 共440页
点击查看更多>>
资源描述

《数据结构第三部分.ppt》由会员分享,可在线阅读,更多相关《数据结构第三部分.ppt(440页珍藏版)》请在三一办公上搜索。

1、1,第三部分 集合,集合中的元素互相之间没有关系集合的存储:借用其他容器集合的操作:查找和排序这一部分将介绍查找表排序算法散列表不相交集,2,第7章 集合与静态查找表,集合的基本概念查找的基本概念无序表的查找有序表的查找STL中的静态表,3,集合的基本概念,集合中的数据元素除了属于同一集合之外,没有任何逻辑关系。在集合中,每个数据元素有一个区别于其他元素的唯一标识,通常称为键值或关键字值集合的主要运算:查找某一元素是否存在将集合中的元素按照它的唯一标识排序,4,集合的存储,任何容器都能存储集合常用的表示形式是借鉴于线性表或树唯一一个仅适合于存储和处理集合的数据结构是散列表,5,第7章 集合与静

2、态查找表,集合的基本概念查找的基本概念无序表的查找有序表的查找STL中的静态表,6,查找的基本概念,用于查找的集合称之为查找表 查找表的分类:静态查找表 动态查找表 内部查找外部查找,7,静态查找表的存储,静态查找表可以用顺序表存储。如C+的标准模板库中的类模板vector,或第二章中定义的seqList,或直接存储在C+的原始数组中。查找函数应该是一个函数模板。模板参数是数据元素的类型。,8,第7章 集合与静态查找表,集合的基本概念查找的基本概念无序表的查找有序表的查找STL中的静态表,9,无序表的查找,无序表:数组中的元素是无序的无序表的查找:毫无选择只能做线性的顺序查找,template

3、 int seqSearch(vector,10,第7章 集合与静态查找表,集合的基本概念查找的基本概念无序表的查找有序表的查找STL中的静态表,11,有序表的查找,顺序查找二分查找插值查找分块查找,12,顺序查找,与无序表的顺序查找类似,只是当被查元素在表中不存在时,不需要查到表尾,template int seqSearch(vector,13,有序表的查找,顺序查找二分查找插值查找分块查找,14,二分查找,每次检查待查数据中排在最中间的那个元素如中间元素等于要查找的元素,则查找完成否则,确定要找的数据是在前一半还是在后一半,然后缩小范围,在前一半或后一半内继续查找。,15,查找 x=8,

4、16,template int binarySearch(const vector,17,有序表的查找,顺序查找二分查找插值查找分块查找,18,插值查找,适用于数据的分布比较均匀的情况查找位置的估计 缺点:计算量大,19,插值查找适用情况,访问一个数据元素必须比一个典型的指令费时得多。例如,数组可能在磁盘里而不是在内存里,而且每次比较都需要访问一次磁盘。这些数据必须不仅有序,而且分布相当均匀,这可以使每次估计都相当准确,可以将大量的数据排除出查找范围。这样,花费这些计算代价是值得的,20,有序表的查找,顺序查找二分查找插值查找分块查找,21,分块查找,分块查找也称为索引顺序块的查找。是处理大量

5、数据查找的一种方法。它把整个有序表分成若干块,块内的数据元素可以是有序存储,也可以是无序的,但块之间必须是有序的。,22,查找由两个阶段组成:查找索引和查找块,23,第7章 集合与静态查找表,集合的基本概念查找的基本概念无序表的查找有序表的查找STL中的静态表,24,STL中的静态表,对应于顺序查找和二分查找,C+的标准模板库中提供两个模板函数:find和binary_search。这两个函数模板都位于标准库algorithm中 find函数顺序查找一个序列find有两个模板参数。第一个模板参数是对应于存储集合数据的容器的迭代器。第二个模板参数是集合元素的类型。函数有三个形式参数。第一、二个形

6、式参数是两个迭代器对象,指出查找的范围。第三个参数是需要查找的对象值。find函数的返回值是一个迭代器对象,指出了被查找的元素在容器中的位置。,25,binary_search,binary_search函数用二分查找的方式查找一个有序序列。它也有两个模板参数。第一个模板参数是对应于存储集合数据的容器的迭代器。第二个模板参数是集合元素的类型。函数也有三个形式参数。第一、二个形式参数是两个迭代器对象,指出查找的范围。第三个参数是需要查找的对象值。函数的返回值是bool类型的,指出了被查找的元素在容器中是否存在。如果存在,返回true,否则,返回false。,26,应用实例,#include#in

7、clude#include using namespace std;int main()int a=2,4,7,8,10,12,13,15,16,19,20;vector v;vector:iterator itr;for(int i=0;i11;+i)v.push_back(ai);if(binary_search(v.begin(),v.end(),13)cout 13 存在n;else cout 13 不存在n;itr=find(v.begin(),v.end(),13);cout*itr endl;return 0;,27,总结,本章介绍了集合关系的基本概念,以及集合类型的数据结构中的

8、基本操作。针对静态的集合,介绍了查找操作的实现。包括顺序查找、二分查找、插值查找和分块查找。最后还介绍了STL中的查找函数:find和binary_search。find实现了顺序查找,binary_search实现了二分查找。,28,第8章 动态查找表,二叉查找树AVL树红黑树AA树伸展树B树和B+树STL中的动态查找表,既要支持快速查找,又要支持插入删除,通常选用树作为存储的载体,29,二叉查找树,二叉查找树的定义二叉查找树的操作二叉查找树的性能二叉查找树类的实现,30,二叉查找树,如果p的左子树若非空,则左子树上的所有结点的关键字值均小于p结点的关键字值。如果p的右子树若非空,则右子树上

9、的所有结点的关键字值均大于p结点的关键字值。结点p的左右子树同样是二叉查找树。,二叉查找树是二叉树在查找方面的重要应用。二叉查找树或者为空,或者具有如下性质:对任意一个结点p而言,31,e、g:二叉查找树,32,二叉查找树,二叉查找树的定义二叉查找树的操作二叉查找树的性能二叉查找树类的实现,33,二叉查找树的操作,特定节点在树上是否存在插入一个节点删除一个节点,由于树是递归定义的,因此这些操作往往也用递归实现。因为二叉树的平均高度为logN,这些操作的时间复杂度也是O(logN),34,查找过程,若根结点的关键字值等于查找的关键字,成功。否则,若关键字值小于根结点,查其左子树。若关键字值大于根

10、结点,查其右子树。在左右子树上的操作类似。,35,如:查找122,查找110,查找230,查找225,36,查找过程的递归描述,如果树为空,返回false如果根节点等于被查结点,返回true如果被查节点小于根节点,递归查找左子树,否则递归查找右子树,37,插入操作,若二叉树为空。则新插入的结点成为根结点。如二叉树非空首先执行查找算法,找出被插结点的父亲结点。判断被插结点是其父亲结点的左、右儿子。将被插结点作为叶子结点插入。,注意:新插入的结点总是叶子结点,38,将数的序列:122、99、250、110、300、280 作为二叉查找树的结点的关键字值,生成二叉查找树。,122,250,300,1

11、10,280,99,39,插入操作的递归实现,如果当前树为空,插入值作为树的根节点,返回如果插入值小于根节点,插入到左子树,否则插入到右子树。,40,执行实例:插入值为 280 的结点,41,删除操作,删除叶结点删除有一个儿子的结点删除有两个儿子的结点,42,删除叶结点,直接删除,更改它的父亲结点的相应指针字段为空。这不会改变二叉查找树的特性。如:删除数据字段为 15、70 的结点。,43,删除操作,删除叶结点删除有一个儿子的结点删除有两个儿子的结点,44,只有一个儿子,122,250,300,200,230,216,400,450,500,45,122,250,110,200,99,105,

12、400,450,500,46,若被删结点只有一个唯一的儿子,将此儿子取代被删结点的位置。即,如被删结点是其父结点的左孩子,那么将他的儿子作为父结点的左孩子;如被删结点是其父结点的有孩子,那么将他的孩子作为父结点的右孩子。能保持二查查找树的有序性,47,删除操作,删除叶结点删除有一个儿子的结点删除有两个儿子的结点,48,被删结点有两个儿子,通常的做法:选取“替身”取代被删结点。有资格充当该替身的是谁哪?替身的要求:维持二叉查找树的特性不变。因此,只有在中序周游中紧靠着被删结点的结点才有资格作为“替身”,即,左子树中最大的结点 或 右子树中最小的结点。,删除这个结点会使其他结点从树上脱离。,49,

13、250,300,200,99,105,330,316,400,450,500,被删结点,替身,做法:将替身110的数据字段复制到被删结点的数据字段。将结点 110 的左儿子作为 99 的右儿子。,用左子树中的最大值做替身,122,110,50,250,300,110,99,105,330,316,400,450,500,被删结点,替身,做法:将替身的数据字段复制到被删结点的数据字段。,用右子树的最小值做替身,122,200,51,结论:先将替身的数据字段复制到被删结点将原替身的另一儿子作为它的父亲结点的儿子,究竟是作为左儿子还是右儿子依原替身结点和其父亲结点的关系而定释放原替身结点的空间。,5

14、2,删除总结,PL、PR皆 空,直接删除。,PL或PR为空,PL为空,删除后的情况。,53,用左子树的最大值代替,54,删除的递归实现,如果是空树,返回如果被删节点小于根节点,递归在左子树上删除如果被删节点大于根节点,递归在右子树删除如果被删节点等于根节点如果根节点有两个儿子,将右子树的最小值放入根节点,在右子树上删除最小节点如果只有左子树,左子树取代根节点,删除原来的根节点如果只有右子树,右子树取代根节点,删除原来的根节点,55,二叉查找树,二叉查找树的定义二叉查找树的操作二叉查找树的性能二叉查找树类的实现,56,二叉查找树性能,二叉查找树的操作(包括insert、find和delete等)

15、的代价正比于操作过程中要访问的结点数。如果所操作的二叉查找树是完全平衡的,那么访问的代价将是对数级别的 在最坏的请况下,二叉查找树会退化为一个单链表。时间复杂度是O(N)。,57,平均性能,n 个结点二叉查找树的可能有n种形态,如果这些形态出现的概率是相等的。设P(n)为查找n个结点的二叉查找树的平均查找时间,则,58,二叉查找树,二叉查找树的定义二叉查找树的操作二叉查找树的性能二叉查找树类的实现,59,二叉排序树类的设计,采用标准的二叉链表存储一棵二叉查找树需要一个指向根节点的数据成员公有的成员函数:find、insert和remove 以及构造、析构函数二叉查找树的插入、删除和查找都是通过

16、递归实现的,而这三个公有函数的参数表中并不需要包含递归参数。为此,对于每个公有的成员函数都定义了一个对应的带有递归参数的私有的成员函数,60,二叉排序树类的定义,template class BinarySearchTreeprivate:struct BinaryNode Type data;BinaryNode*left;BinaryNode*right;BinaryNode(const Type,61,public:BinarySearchTree(BinaryNode*t=NULL)root=t;BinarySearchTree();bool find(const Type,62,二叉

17、排序树类的设计特点,一个内嵌的BinaryNode类数据成员只有一个,树根公有的成员函数通过调用私有的递归函数完成相应的功能,63,find函数的实现,template bool BinarySearchTree:find(const Type,64,insert函数的实现,template void BinarySearchTree:insert(const Type,65,insert函数设计说明,私有的insert函数的第二个形式参数,它是一个指向结点的指针的非常量引用。这个引用符号不能省略。正是这个引用,使得新插入的结点和它的父结点之间关联了起来。,66,remove函数的实现,tem

18、plate void BinarySearchTree:remove(const Type,67,template void BinarySearchTree:remove(const Type,68,remove函数设计说明,同样请注意私有的remove函数的参数,它的第二个参数也是指向结点的指针的引用。与插入过程一样,这个引用也不能去掉。正是这个引用使得被删结点的父结点和被删结点的儿子连接起来。,69,二叉查找树类的使用,int main()int a=10,8,6,21,87,56,4,0,11,3,22,7,5,34,1,2,9;BinarySearchTree tree;for(in

19、t i=0;i 17;+i)tree.insert(ai);cout endl find 2 is(tree.find(2)?true:false)endl;tree.remove(2);cout after delete 2,find 2 is(tree.find(2)?true:false)endl;cout find 3 is(tree.find(3)?true:false)endl;tree.remove(3);cout after delete 3,find 3 is(tree.find(3)?true:false)endl;cout find 21 is(tree.find(21)

20、?true:false)endl;tree.remove(21);cout after delete 21,find 21 is(tree.find(21)?true:false)endl;return 0;,70,71,第8章 动态查找表,二叉查找树AVL树红黑树AA树伸展树B树和B+树STL中的动态查找表,72,AVL树,AVL树的定义AVL树的插入操作AVL树的删除AVL树类的实现,73,平衡二叉查找树,当树退化为链表时,树的优点荡然无存。要使查找树的性能尽可能好,就要使得树尽可能丰满。要构造一个丰满树很困难,一种替代的方案是平衡树。,74,二叉平衡树,二叉平衡树就是满足某种平衡条件的二

21、叉查找树平衡条件:容易维护保证树的高度是O(logN),75,平衡条件,最简单的想法是让左右子树有同样的高度。但它并不能保证树是矮胖的。另一个想法是要求每个节点的左右子树都有同样的高度。这个条件能保证树是矮胖的,但这个条件太苛刻,只有满二叉树才满足这个条件。将上述条件稍微放宽一些就是二叉平衡查找树。,76,平衡树的定义,平衡因子(平衡度):结点的平衡度是结点的左子树的高度右子树的高度。空树的高度定义为-1。平衡二叉树:每个结点的平衡因子都为 1、1、0 的二叉树。或者说每个结点的左右子树的高度最多差1的二叉树。可以证明平衡树的高度至多约为:1.44log(N+2)-1.328,77,是平衡树不

22、是丰满树,不是平衡树,78,平衡二叉树的查找性能,定理:具有 N 个结点的平衡树,高度 h 满足:log2(N+1)=h=1.44log2(N+1)-0.328;,与二叉树的高度成正比,因此,平衡二叉树的操作都是O(logN),79,AVL树,AVL树的定义AVL树的插入操作AVL树的删除AVL树类的实现,80,插入操作,插入过程与二叉查找树的插入相同,只是插入后可能出现两种情况:插入后,不破坏平衡性,只是改变了树根到插入点的路径上的某些结点的平衡度,因此需要自底向上修改节点的平衡度破坏了路径上的某些结点的平衡性,需要向上调整树的结构,81,14,12,5,3,9,28,63,53,60,50

23、,17,18,7,30,+1,+1,-1,-1,-1,0,0,0,0,0,0,0,0,只改变了某些结点的平衡度,需要自底向上的调整。只要有一个节点的平衡度不变,它上面的节点的平衡度也不变。调整可以结束。,插入29,0,+1,0,82,平衡树,插入2以后,变得不平衡了。如何用最简单、最有效的办法保持平衡分类二叉树的性质不变?,83,调整要求:希望不涉及到危机结点的父亲结点,即以危机结点为根的子树的高度应保持不变。调整可以到此结束。仍应保持分类二叉树的性质不变,84,重新平衡,如果节点原来的平衡度为0,则插入后不可能失衡,重新计算平衡度,继续往上回溯如果节点原来的平衡度非0,可能成为失衡节点重新计

24、算平衡度如果平衡度在合法范围,调整结束如果失去平衡,重新调整树的结构,调整结束,从插入位置向根回溯,85,引起节点不平衡的情况,原结点的左子树高,在结点的左孩子的左子树上插入(LL)原结点的左子树高,在结点左孩子的右子树上插入(LR)原结点的右子树高,在结点的右孩子的左子树上插入(RL)原结点的左子树高,在结点的右孩子的右子树上插入(RR),86,引起不平衡的情况,LLRR:LL的镜像对称,LRRL:LR的镜像对称,87,LL和RR问题,通过单旋转来解决。即危机结点和引起危机的儿子的角色转换。如果是LL问题,将危机结点的左儿子作为根,原来的根结点作为他的右子树,原先的右儿子作为原先根的左儿子。

25、如果是RR问题,将危机结点的右儿子作为根,原来的根结点作为他的左子树,原先的左儿子作为原先根的右儿子,88,LL问题,保持了树的有序性保持了原先的高度,89,在下列树中插入4,将会使得9失去平衡。这是在9的左孩子的左子树上插入引起失衡,是LL情况,90,旋转后的结果,保持了树的有序性保持了原先的高度,91,LR和RL问题,通过双旋转来解决,即两次单旋转。先对危机结点的儿子和孙子进行一次单旋转,使孙子变成儿子。然后是危机结点和新的儿子进行一次单旋转。,92,LR问题,有一个结点被插入到B的右子树的事实保证了B的右子树不会是空的,因此我们可以假定B的右子树为C,它有一个根和两棵子树(当然可能是空的

26、)组成,93,保持了树的有序性保持了原先的高度,LR旋转可以看成有两个单选转组成:先对B执行RR旋转,再对A执行LL旋转,94,插入4后,调整后,95,AVL插入总结,用递归实现要在AVL树T中插入一个键值为X的结点,我们递归地将它插入到T的某棵合适的子树(记做TLR)中,如果插入后TLR的高度没有改变,就完成了操作。否则,我们就根据X和T及TLR中的键值选择单旋转或是双旋转(以T为根),然后操作也结束了(因为原来的高度和旋转后的高度是一样的),96,AVL树,AVL树的定义AVL树的插入操作AVL树的删除AVL树类的实现,97,平衡二叉树的删除,首先在AVL树上删除结点x然后调整平衡,98,

27、调整平衡,和插入操作一样,失衡节点存在于被删节点到根节点的路径上在删除了一个结点后,必须沿着到根结点的路径向上回溯,随时调整路径上的结点的平衡度。删除操作没有插入操作那么幸运。插入时,最多只需要调整一个结点。而删除时,我们无法保证子树在平衡调整后的高度不变。只有当某个结点的高度在删除前后保持不变,才无需继续调整。递归的删除函数有一个布尔型的返回值。当返回值为true时,调整停止。当返回值为false时,继续调整。,99,删除可能出现的情况,令q是最终被删除的结点,p是q到根结点的路径上的某个结点。q的删除对p的影响有以下几种情况:结点P的平衡因子原为0 从p的较高的子树上删去一个结点 从p较矮

28、的子树上删除一个结点,100,结点P的平衡因子原为0,从p的某棵子树上删除结点后,该子树变矮,但以p为根的子树高度不会改变。只需要修改p的平衡因子即可。p的高度不变,意味着它上面的结点的平衡因子都不变,调整可以结束。返回true。,101,从p的较高的子树上删去一个结点,从p的较高的子树上删除结点后,如果该子树变矮,以p为根的子树也变矮。但以结点P为根的这棵子树仍然是AVL树,而且更平衡了。调整:将p的平衡因子置为0由于以p为根的子树变矮,可能会影响p的父结点的平衡度,返回false,102,删除2:原来结点5是左高右低,属于情况2。删除2以后,结点5的平衡因子变为0,以结点5为根结点的子树也

29、矮了一层,这样就会影响结点7的平衡度,所以继续往上调整。对结点7而言,正好属于情况一。所以修改7的平衡因子,调整结束。,103,从p较矮的子树上删除一个结点,在p的较矮的子树上删除一个结点,使较矮的子树变得更矮,p的平衡度肯定遭到破坏,此时要通过旋转来恢复这棵子树的平衡。设q是p的较高的子树的根。根据q的平衡因子的值,可以进一步细化为以下三种不同的情况:q的平衡因子为0q的平衡因子和p的平衡因子符号相同 q的平衡因子和p的平衡因子符号相反,104,Q的平衡因子为0,整棵树高度不变,不需要继续调整,105,q和p的平衡因子符号相同,整棵树矮了一层,需要继续调整,106,q和p的平衡因子符号相反,

30、整棵树矮了一层,需要继续调整,107,AVL树,AVL树的定义AVL树的插入操作AVL树的删除AVL树类的实现,108,AVL树类的实现,template class AvlTree struct AvlNode Type data;AvlNode*left;AvlNode*right;int height;/结点的高度 AvlNode(const Type,109,public:AvlTree(AvlNode*t=NULL)root=t;AvlTree()makeEmpty(root);bool find(const Type,110,find函数的非递归实现,template bool A

31、vlTree:find(const Type,111,LL,template void AvlTree:LL(AvlNode*,112,RR,template void AvlTree:RR(AvlNode*,A,B,-1,h-1,0,-2,-1,h-1,h-1,BR,BL,AL,危机结点,113,LR和RL,template void AvlTree:LR(AvlNode*,114,私有的insert函数的实现,template void AvlTree:insert(const Type,115,私有的remove函数的实现,template bool AvlTree:remove(con

32、st Type,116,if(x data)stop=remove(x,t-left);subTree=1;else if(t-data right);subTree=2;else if(t-left!=NULL,117,switch(subTree)case 1:bf=height(t-left)-height(t-right)+1;/原来结点的平衡度 if(bf=0)return true;/情况一 if(bf=1)return false;/情况二 if(bf=-1)/情况三 int bfr=height(t-right-left)-height(t-right-right);switc

33、h(bfr)case 0:RR(t);return true;case-1:RR(t);return false;default:RL(t);return false;break;,118,case 2:bf=height(t-left)-height(t-right)-1;if(bf=0)return true;/情况一 if(bf=-1)return false;/情况二 if(bf=1)/情况三 int bfl=height(t-right-left)-height(t-right-right);switch(bfl)case 0:LL(t);return true;case 1:LL(

34、t);return false;default:LR(t);return false;,119,第8章 动态查找表,二叉查找树AVL树红黑树AA树伸展树B树和B+树STL中的动态查找表,120,红黑树,红黑树的定义红黑树的插入红黑树的删除红黑树类的实现,红黑树是平衡树的一种替换方案。它比平衡树效率高。红黑树在最坏情况下的操作时间是O(logN),121,红黑树的定义,每个节点都被标记为红色或是黑色的根是黑色的如果一个节点是红色的,那么它的儿子都是黑色的每一条从某个节点到一个空链域的路径必须具有相同数量的黑色节点,122,红黑树,红黑树的定义红黑树的插入红黑树的删除红黑树类的实现,红黑树是平衡树

35、的一种替换方案。它比平衡树效率高。红黑树在最坏情况下的操作时间是O(logN),123,红黑树的插入,插入过程同普通的二叉查找树,只是插入后被插入的节点要被着色着成黑色是不可能的,会违反定义4,必须着成红色如果父节点是黑色的,插入结束。如果父节点是红色的,则违反定义3。必须调整节点的颜色把新插入的节点称作X,P是它的父亲节点,S是它父亲的兄弟节点(空节点认为是黑色的),G是X祖父。,124,颜色调整总结,父结点P的兄弟结点S是黑色的X是G的外侧结点:LLb,RRb X是G的内侧结点:LRb,RLb 父结点P的兄弟结点S是红色的,125,LLb,RRb,D,E,C,A,G,P,B,X,S,RRb

36、旋转,D,E,C,A,P,X,B,S,G,调整后,树根为黑色。不需要继续调整,126,LRb,RLb,LRb旋转,B,D,E,C,A,G,S,B,P,D,E,C,A,X,G,S,RLb旋转,X,P,调整后,树根为黑色。不需要继续调整,127,在树中插入1,属LLb的情况,128,父结点P的兄弟结点S是红色的,通过重新着色,消除连续红节点,129,通过重新着色,连续的红结点就不存在了,而且路径上的黑结点数也没有变化。经过重新着色以后,根结点变成了红色。如果原来X的曾祖父就是红色的,那么又出现了连续红结点问题。于是,需要继续往上调整。最坏的情况下可能要调整到根。,130,插入24,重新着色,调整2

37、0、25的连续红节点,又是情况二。重新着色,131,红黑树,红黑树的定义红黑树的插入红黑树的删除红黑树类的实现,红黑树是平衡树的一种替换方案。它比平衡树效率高。红黑树在最坏情况下的操作时间是O(logN),132,红黑树的删除,删除操作首先使用普通的二叉查找树的删除算法删除结点,然后进行旋转及颜色的调整。在二叉查找树的删除中,最终可以归结到两种情况:删除叶结点和删除只有一个儿子的结点。只要解决了这两种情况下的着色问题,就解决了红黑树的删除,133,红黑树删除时的情况,删除的是红色叶结点:直接删除 删除只有一个儿子的结点:将儿子的颜色改为黑色 删除一个黑色的叶结点,134,删除一个黑色的叶结点,

38、被调整的结点的兄弟是黑色的(如果兄弟是空结点,则认为是黑色结点)被调整的结点的兄弟是红色的,删除这个结点将会使得这个位置变成了一个空结点。因此,这条路径上少了一个黑结点。我们将这个空结点称为被调整结点。,135,被调整的结点的兄弟是黑色的,L0和R0:兄弟结点有0个红孩子 L1L和R1R:兄弟有一个红孩子,且为它的外侧的孩子 L1R和R1L:兄弟有一个红孩子,且为它的内侧的孩子 L2和R2:兄弟有两个红孩子,136,L0和R0,父节点原来可以为任意颜色。如果父结点原来是红色的,现在变成了黑色,调整可以结束了。但如果父结点原来就是黑色的,现在经过父结点的所有路径都少了一个黑结点。于是继续调整。,

39、137,L1L和R1R,新的根结点的颜色和老的根结点的颜色是相同的,因此也不会出现连续的红结点的问题。调整可以结束了,138,L1R和R1L,由于新的根结点的颜色和老的根结点的颜色是相同的,因此也不会出现连续的红结点的问题。调整可以结束了,139,L2和R2,L2的调整方法和L1R是一样的,R2的调整方法和R1L是一样的,140,删除一个黑色的叶结点,被调整的结点的兄弟是黑色的(如果兄弟是空结点,则认为是黑色结点)被调整的结点的兄弟是红色的,删除这个结点将会使得这个位置变成了一个空结点。因此,这条路径上少了一个黑结点。我们将这个空结点称为被调整结点。,141,兄弟结点是红色的,如果被调整结点的

40、兄弟结点是红色的,那么父结点一定是黑色的,兄弟结点的儿子也一定是黑色的。旋转后,红黑树的特性得以保持,但被调整结点的兄弟结点变成了黑色。第2种情况转换成了第1种情况,,142,在下列红黑树中删除26,违反了红黑树的性质,25的右路径少了一个黑结点,是属于第二种情况,143,旋转后,结果如下,被调整节点(25的右孩子)的兄弟变成了黑色而且有一个红孩子,属于L1R的情况。,调整后,这棵树恢复了平衡,继续调整,144,红黑树,红黑树的定义红黑树的插入红黑树的删除红黑树类的实现,红黑树是平衡树的一种替换方案。它比平衡树简单。红黑树在最坏情况下的操作时间是O(logN),145,红黑树类的实现,红黑树的

41、节点类需要一个保存颜色的数据成员插入、删除时的调整需要用到父节点可祖父节点,所以用迭代的方式实现,146,红黑树类的定义,template class RedBlackTree struct RedBlackNode Type data;RedBlackNode*left;RedBlackNode*right;int colour;/0-Red,1-Black RedBlackNode(const Type,147,public:RedBlackTree(RedBlackNode*t=NULL)root=t;RedBlackTree()makeEmpty(root);bool find(con

42、st Type,148,reLink函数的实现,当子树被旋转后,子树的根会发生变化。对子树的父结点来讲,必须将新的子树根作为儿子。reLink就是完成这个功能ReLink函数有三个参数。第一个参数是子树原来的根,第二个参数是子树的新根,第三个参数是一个链接栈类对象,保存从根结点到这棵子树的路径上的结点。这里的linkStack就是第三章中实现的链接栈类,149,template void RedBlackTree:reLink(RedBlackNode*oldp,RedBlackNode*newp,linkStack,150,Find、makeEmpty和旋转函数,与AVL树完全相同,151,

43、insert函数的实现,用非递归实现插入函数只需要一个参数:被插入的节点需要维护一个栈,保存从根节点到当前节点的路径可以用第三章中的栈类,152,template void RedBlackTree:insert(const Type,153,if(t!=NULL)return;t=new RedBlackNode(x,NULL,NULL);parent=path.pop();if(x data)parent-left=t;else parent-right=t;if(parent-colour=1)return;path.push(parent);insertReBalance(t,path

44、);,154,insertReBalance(t,path),template void RedBlackTree:insertReBalance(RedBlackNode*t,linkStack,155,while(parent-colour=0)if(parent=root)parent-colour=1;return;grandParent=rootOfSubTree=path.pop();if(grandParent-data parent-data)uncle=grandParent-right;else uncle=grandParent-left;if(uncle=NULL|un

45、cle-colour=1)/情况一的处理 else/情况二 grandParent-colour=0;parent-colour=1;uncle-colour=1;if(root=grandParent)root-colour=1;return;t=grandParent;parent=path.pop();,156,/情况一的处理,If(grandParent-left=parent)if(t=parent-left)/LLb parent-colour=1;grandParent-colour=0;LL(grandParent);else/LRb grandParent-colour=0;

46、t-colour=1;LR(grandParent);else if(t=parent-right)/RRb parent-colour=1;grandParent-colour=0;RR(grandParent);else/RLb grandParent-colour=0;t-colour=1;RL(grandParent);reLink(rootOfSubTree,grandParent,path);return;,157,Remove函数,使用迭代的方式实现删除操作。remove函数中也设置了一个栈path,保存从根结点到被删结点的路径。删除函数首先执行结点删除,然后判断红黑树是否失衡。

47、如果失衡,则调用removeReBalance重新调整平衡。,158,remove函数的实现,template void RedBlackTree:remove(const Type,159,/执行删除 if(t=root)/删除根结点 root=(t-left?t-left:t-right);if(root!=NULL)root-colour=1;return;parent=path.pop();old=t;t=(t-left?t-left:t-right);if(parent-left=old)parent-left=t;else parent-right=t;if(old-colour=

48、0)delete old;return;/删除红叶结点 delete old;if(t!=NULL)/有一个红儿子 t-colour=1;return;path.push(parent);removeReBalance(t,path);,160,removeReBalance函数的实现,template void RedBlackTree:removeReBalance(RedBlackNode*t,linkStack,161,while(parent!=NULL)if(parent-left=t)sibling=parent-right;else sibling=parent-left;if

49、(sibling-colour=0)/情况二 sibling-colour=1;parent-colour=0;if(parent-left=t)RR(parent);else LL(parent);reLink(rootOfSubTree,parent,path);path.push(parent);parent=rootOfSubTree;else/兄弟是黑结点 if(sibling-left=NULL|sibling-left-colour=1)/end of while,162,/情况一的处理if(parent-left=t)/兄弟是右孩子 if(sibling-left!=NULL,

50、163,第8章 动态查找表,二叉查找树AVL树红黑树AA树伸展树B树和B+树STL中的动态查找表,164,AA树,AA树的定义 AA树的插入操作 AA树的删除操作 AA树类的实现,165,AA树,定义:左孩子不能为红色的红黑树优点:它省去了一半的需要重构的情况;简化了重新平衡的过程,166,一棵AA树,167,AA树的水平链表示法,将指向红结点的链画成水平的,可以看出所有叶子的高度都是相同的,168,用水平链表示的AA树的特征:,水平链是右儿子链(因为只有右儿子才可能是红色的)不可能有左水平链(因为做儿子不可能为红色)不可能有两条连续的水平链(因为不会有连续的红色结点)如果一个结点没有右水平链

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 建筑/施工/环境 > 农业报告


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号