1、1,1静态查找表 1.1顺序表的查找 1.2有序表的查找 2动态查找表 2.1二叉排序树和平衡二叉树(AVL树),查找,2,3哈希表 3.1什么是哈希表 3.2哈希函数的构造方法 3.3处理从冲突的方法 3.4哈希表的查找及分析,3,查找(Search)的概念,查找:就是在数据元素集合中寻找满足某种条件的数据对象。 查找表(Search Table):是由同一类型的数据元素(或记录)组成的数据集合。,由于“集合”中的数据元素之间存在着松散的关系,因此查找表是一种应用灵便的结构。,4,对查找表经常进行的操作:,1)查询某个“特定的”数据元素是否在查找表中; 2)检索某个“特定的”数据元素的各种属
2、性; 3)在查找表中插入一个数据元素; 4)从查找表中删去某个数据元素。,5,仅作查询和检索操作的查找表。,静态查找表,有时在查询之后,还需要将“查询”结果为“不在查找表中”的数据元素插入到查找表中;或者,从查找表中删除其“查询”结果为“在查找表中”的数据元素。,动态查找表,查找表可分为两类:,6,是数据元素(或记录)中某个数据项的值,用以标识(识别)一个数据元素(或记录)。,关键字(Key),若此关键字可以识别唯一的一个记录,则称之谓“主关键字(Primary Key)”。,若此关键字能识别若干记录,则称之谓“次关键字(Secondary Key)”。 当数据元素只有一个数据项时,其关键字即
3、为该数据元素的值。,使用基于主关键字的查找,查找结果应是唯一的。,7,查找的结果通常有两种可能: 查找成功,即找到满足条件的数据对象。 查找结果:给出整个记录的信息,或指示该记录在查找表中的位置。 查找不成功,或查找失败。作为结果,报告一些信息,如失败标志、失败位置等。 查找结果:给出“空记录”或“空指针”。,8,典型的关键字类型说明可以是:typedef float KeyType; / 实型typedef int KeyType; / 整型typedef char KeyType; / 字符 型typedef char* KeyType; / 字符串型,数据元素类型定义为:typedef
4、struct KeyType key; / 关键字域 / 其他域ElemType;,9,1 静态查找表,数据对象D:,数据关系R:,D是具有相同特性的数据元素的集合。每个数据元素含有类型相同的关键字,可唯一标识数据元素。,数据元素同属一个集合。,ADT StaticSearchTable ,10,基本操作P: Create(&ST, n) 操作结果:构造一个含 n 个数据元素的静态查找表ST。Destroy(&ST) 初始条件:静态查找表 ST 存在。操作结果:表 ST 被销毁。,11,Search( ST, key )初始条件:静态查找表 ST 存在,key为和关键字类型相同的给定值。操作结
5、果:若ST中存在其关键字等于 key 的数据元素,则函数值为该元素的值或它在表中的位置,否则函数值为空。Traverse( ST, Visit( ) ) 初始条件:静态查找表 ST 存在,Visit 是对元素操作的应用函数。操作结果:按某种次序对ST的每个元素调用函数Visit 一次且仅一次。一旦Visit 失败,则操作失败。,12,所谓顺序查找(Sequiential Search),又称线性查找,主要用于在线性结构中进行查找。 存储结构: typedef structType elem MAX ; /数据元素存储空间基址,建表时按实际长度分配,0号单元留空int length; /表长度
6、STable;,1.1顺序查找,13,查找过程: 从表中最后一个元素开始,顺序用各元素的关键字与给定值x进行比较,若找到与其值相等的元素,则查找成功,给出该元素在表中的位置;否则,若直到第一个记录仍未找到关键字与x相等的对象,则查找失败。,0 1 2 3 4 5 6 7 8 9 10 11,5 13 19 21 37 56 64 75 80 88 92,找64,64,监视哨,比较次数=5,比较次数: 查找第n个元素: 1 查找第n-1个元素:2 . 查找第1个元素: n 查找第i个元素: n+1-i 查找失败: n+1,查找过程:从表的一端开始逐个进行记录的关键字和给定值的比较。 例如:,15
7、,算法(从前往后) Search (STable ST, Type x) /顺序查找的算法,n号元素为监视哨ST.elem0.key =x; /哨兵for (i=1; ST.elemi.key!=x;+i) ;return i ; ,16,讨论:如何评估查找方法的优劣?,时间复杂度,空间复杂度,17,明确:查找的过程就是将给定的Key值与文件中各记录的关键字项进行比较的过程。所以用比较次数的平均值来评估算法的优劣。称为平均查找长度ASL。,其中: n是文件记录个数; Pi是查找第i个记录的查找概率(通常取等概率,即Pi =1/n); Ci是找到第i个记录时所经历的比较次数。,Average S
8、earch Length,ASL=,PiCi,i=1,n,显然,ASL值越小,时间效率越高。,18,物理意义: 假设每一元素被查找的概率相同,则查找每一元素所需的 比较次数之总和再取平均,即为ASL。,19,分析:查找第n个元素所需的比较次数为1; 查找第n-1个元素所需的比较次数为2; 查找第1个元素所需的比较次数为n;,总计全部比较次数为:12n = (1+n)n/2,这是查找成功的情况,若求某一个元素的平均查找次数,还应当除以n(等概率),即: ASLcc(1n)/2 ,时间效率为 O(n)。,20,对于顺序查找,不论给定值key为何值,查找不成功时和给定值进行比较的关键字个数均为n+1
9、。假设查找成功和不成功时的可能性相同,对每个纪录的查找概率也相同,则pi=1/2n。 此时顺序查找的平均查找长度为:,ASLss,=,1,2n,(n-i+1),i=1,n,+,=,3,4,(n+1),1,2n,n(n+1),CiPi,i=1,n,21,1.2有序表的查找,上述顺序查找表的查找算法简单,但平均查找长度较大,特别不适用于表长较大的查找表。,若顺序表中所有记录的关键字满足下列关系: ST.elemi.key ST.elemi+1.key i = 1, 2, , n-1 则称其为有序顺序表,简称有序表。,22,若以有序表表示静态查找表,则查找过程可以基于“折半”进行。,有序顺序表上的查
10、找算法主要有顺序查找和折半查找两种方法。,1.有序顺序表上顺序查找算法类同顺序表上的顺序查找算法。,2.二分查找(又称折半查找) 查找过程:每次将待查记录所在区间缩小一半。 适用条件:采用顺序存储结构的有序表。,1.设表长为n,low、high和mid分别指向待查元素所在区间的上界、下界和中点,k为给定值。 2.初始时,令 low=1, high=n , mid= (low+high)/2 让k与mid指向的记录比较 若 k=rmid.key,查找成功 若 krmid.key,则 low = mid+1 3.重复上述操作,直至lowhigh时,查找失败。,折半查找算法实现,low,high,m
11、id,例如 key =21 的查找过程,low 指示查找区间的下界; high 指示查找区间的上界; mid = (low+high)/2。,例key =70 的查找过程,找70,1 2 3 4 5 6 7 8 9 10 11,5 13 19 21 37 56 64 75 80 88 92,low,high,当下界low大于上界high时,则说明表中没有关键字等于Key的元素,查找不成功。,27,折半查找: (1)mid= (low+high)/2 (2)比较 ST.elemmid.key = = key?如果 ST.elemmid.key = = key,则查找成功,返回mid值如果 ST.
12、elemmid.key key,则置high=mid-1如果 ST.elemmid.key high时,表明查找不成功,查找结束。,int Search_Bin ( STable ST, KeyType k ) low = 1; high = ST.length; / 置区间初值while (low = high) mid = (low + high) / 2;if (k = ST.elemmid.key )return mid; / 找到待查元素else if ( k ST.elemmid.key) )high = mid - 1; / 继续在前半区间进行查找else low = mid +
13、 1; / 继续在后半区间进行查找return 0; / 顺序表中不存在待查元素 ,折半查找算法,29,ST.elem,ST.length,例如: key = 64 的查找过程如下,low,high,mid,low,mid,high,mid,low 指示查找区间的下界; high 指示查找区间的上界; mid = (low+high)/2。,30,int Search_Bin(SSTable ST,KeyType key) /折半查找 int low,high,mid;low = 1; high=ST.length; /置区间初值while (low high) mid = (low+high
14、)/2; if (EQ(key,ST.elemmid.key) return mid; /找到待查元素else if (LT(key,ST.elemmid.key) high=mid-1; /继续在前半区间进行查找else low=mid+1; /继续在后半区间进行查找return 0; /顺序表中不存在待查元素 /Search_Bin,31,32,先看一个具体的情况,假设:n=11,分析折半查找的平均查找长度,1,2,2,3,3,3,3,4,4,4,4,low,high,mid,low,high,mid,low,high,mid,low,high,mid,low,high,mid,low,h
15、igh,mid,low,high,mid,low,mid,high,low,mid,high,low,mid,high,low,mid,high,33,6,3,9,1,4,2,5,7,8,10,11,判定树,从有序表构造出的二叉查找树(判定树),34,第1层结点有1个,查找第1层结点要比较1次;第2层结点有2个,查找第2层结点要比较2次;,,一般情况下,表长为 n 的折半查找的判定树的深度和含有 n 个结点的完全二叉树的深度相同。 h = log2n+1,若设 n = 2h-1,则描述二分查找的二叉查找树是高度为 h 的满二叉树。2h = n+1, h = log2(n+1)。,35,找到有序
16、表中任一记录的过程就是:走了一条从根结点到与该记录对应结点的路径。 比较的关键字个数为:该结点在判定树上的层次数。 查找成功时比较的关键字个数最多不超过树的深度 d :d = log2 n + 1 查找不成功的过程就是:走了一条从根结点到外部结点的路径。,int Search_Bin ( STable ST, KeyType k ) low = 1; high = ST.length; / 置区间初值while (low = high) mid = (low + high) / 2;if (k = ST.elemmid.key )return mid; / 找到待查元素else if ( k
17、ST.elemmid.key) )high = mid - 1; / 继续在前半区间进行查找else low = mid + 1; / 继续在后半区间进行查找return 0; / 顺序表中不存在待查元素 ,折半查找算法,先看一个有11个元素的表的例子:n=11,6,3,9,1,4,2,5,7,8,10,11,折半查找的性能分析,判定树:描述查找过程的二叉树。 有n个结点的判定树的深度为log2 n+1 折半查找法在查找过程中进行的比较次数最多不超过log2 n+1,假设 有序表的长度n=2h-1(反之h=log2(n+1) ),则描述折半查找的判定树是深度为h的满二叉树。树中层次为1的结点有
18、1个,层次为2的结点有2个,层次为h的结点有2 h-1个。假设表中每个记录的查找概率相等 则查找成功时折半查找的平均查找长度,在 n50 时,可得近似结果,可见, 折半查找的效率比顺序查找高。 折半查找只能适用于有序表,并且以顺序存储结构存储。,顺序表和有序表的比较,41,思考题: 问:能否使用单链表结构进行折半查找?,课堂练习1(多选题)使用折半查找算法时,要求被查文件:,采用链式存贮结构 记录的长度12 采用顺序存贮结构 记录按关键字递增有序,课堂练习2 在有序线性表a20上进行折半查找,则查找成功时平均查找长度为 。,课堂练习2 在有序线性表a20上进行折半查找,则查找成功时平均查找长度
19、为 3.7 。 (1*1+2*2+4*3+8*4+5*5)/20,无法实现!因全部元素的定位只能从头指head开始,42,i,ci,1,4,2,3,3,4,4,5,5,2,6,4,7,3,8,4,9,5,10,1,11,4,12,3,13,4,14,5,15,2,16,4,17,5,18,3,19,4,20,5,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,l,h,m,43,10,15,5,7,2,18,12,3,
20、1,4,8,6,9,13,11,14,19,16,20,17,1,2*2,4*3,8*4,5*5,1 2 4 8 5,44,1)从查找性能看,最好情况能达(log2n),此时要求表有序;,2)从插入和删除的性能看,最好情况能达(1),此时要求存储结构是链表。,可得如下结论:,3 索引表查找,在建立顺序表的同时,建立一个索引表,包括两项:关键字项和指针项。索引表按关键字有序,表则为分块有序,索引顺序表 = 索引 + 顺序表,顺序表,索引表,索引顺序查找又称分块查找 查找过程:将表分成几块,块内无序,块间有序;先确定待查记录所在块,再在块内查找 适用条件:分块有序表 算法实现: 用数组存放待查记录
21、,每个数据元素至少含有关键字域 建立索引表,每个索引表结点含有最大关键字域和指向本块第一个结点的指针,1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18,22 12 13 8 9 20 33 42 44 38 24 48 60 58 74 57 86 53,22 48 86,1 7 13,索引表,查38,例如,分块查找方法评价,查找方法比较,顺序查找,折半查找,分块查找,(n) (1) (n) (1),几种查找表的特性,查找 插入 删除,无序顺序表 无序线性链表 有序顺序表 有序线性链表,(n) (n) (logn) (n),(1) (1) (n) (1)
22、,从查找性能看,最好情况能达(logn),此时要求表有序; 从插入和删除的性能看,最好情况能达(1),此时要求存储结构是链表。,结论:,52,53, 2 动态查找表,抽象数据类型动态查找表的定义如下:,ADT DynamicSearchTable 数据对象D: D是具有相同特性的数据元素的集合。各个数据元素均含有类型相同,可唯一标识数据元素的关键字。 数据关系R: 数据元素同属一个集合。 基本操作P: InitDSTable( &DT ) 操作结果:构造一个空的动态查找表 DT。DestroyDSTable( &DT ) 初始条件:动态查找表 DT 存在。操作结果:动态查找表 DT 被销毁。,
23、54,SearchDSTable( DT, key )初始条件:动态查找表 DT 存在,key为和关键字类型相同的给定值。操作结果:若DT中存在其关键字等于key 的数据元素,则函数值为该元素的值或它在表中的位置,否则函数值为空。 InsertDSTable( &DT, e ) 初始条件:动态查找表DT 存在,e 为待插入的数据元素。操作结果:若DT 中不存在其关键字等于 e.key 的数据元素,则插入 e 到 DT。,55,DeleteDSTable( &DT, key )初始条件:动态查找表 DT 存在,key为和关键字类型相同的给定值。操作结果:若DT 中存在其关键字等于key 的数据元
24、素,则删除之。,56,动态查找树表,二叉排序树(二叉查找树),平衡二叉树(AVL树),57,动态查找表的 特点 :,表结构的生成是从空表开始,逐个插入记录而得。在插入一个关键字为 key 的记录之前必须先进行查找,若表中不存在其关键字等于 key 的记录,则插入之,否则不能插入。,58,2.1二叉排序树和平衡二叉树,二叉排序树(二叉查找树),二叉排序树的定义,二叉排序树的查找,二叉排序树的插入,二叉排序树的删除,二叉排序树的查找性能分析,59,1.二叉排序树及其查找过程 定义 二叉排序树(二叉查找树) 或者是一棵空树,或者是具有下列性质的二叉树:每个结点都有一个作为查找依据的关键字(key),
25、所有结点的关键字互不相同。 左子树(若非空)上所有结点的关键字都小于根结点的关键字。 右子树(若非空)上所有结点的关键字都大于根结点的关键字。 左子树和右子树也是二叉排序树。,60,如果对一棵二叉排序树进行中序遍历,可以按从小到大的顺序,将各结点关键字排列起来。,几个二叉排序树的例子,10,20,30,10,30,20,20,30,10,30,20,10,30,20,10,(a),(b),(c),(d),(e),61,例如:,是二叉排序树。,不,62,二叉排序树的查找算法:,1)若给定值等于根结点的关键字,则查找成功; 2)若给定值小于根结点的关键字,则继续在左子树上进行查找; 3)若给定值大
26、于根结点的关键字,则继续在右子树上进行查找。,否则:,若二叉排序树为空,则查找不成功;,以二叉链表形式存储,typedef struct Node / 结点结构Type key;struct Node *lchild, *rchild; / 左右指针 *BiTree ;,(2) 二叉排序树的存储结构,查找算法,若二叉排序树为空,则查找不成功; 否则1)若给定值等于根结点的关键字,则查找成功;2)若给定值小于根结点的关键字,则继续在左子树上进行查找;3)若给定值大于根结点的关键字,则继续在右子树上进行查找。,(3) 操作,65,BiTree SearchBst(BTree T, Type key
27、) /在根指针T所指二叉排序树中递归地查找某关键字等于key的数据元素,若查找成功,则返回指向该数据元素结点的指针,否则返回空指针if(!T)| key= =T-data.key )return(T); /查找结束elseif( key data.key )return(SearchBst(T-lchild, key) /在左子树中继续查找elsereturn(SearchBst(T-rchild, key) /在右子树中继续查找,66,BiTree SearchBst(BTree T, Type key) /在根指针T所指二叉排序树中递归地查找某关键字等于key的数据元素,若查找成功,则返回
28、指向该数据元素结点的指针,否则返回空指针if(!T|key=T-data.key)return(T); /查找结束elseif( keydata.key )return(SearchBst(T-lchild, key) /在左子树中继续查找elsereturn(SearchBst(T-rchild, key) /在右子树中继续查找,if ( ! T ) return NULL; / 查找不成功 else if ( k T- key ) return T ; / 查找成功 else if ( k key )return Search ( T-lchild, k ); / 在左子树中继续查找els
29、e return Search ( T-rchild, k ); / 在右子树中继续查找,Search ( BiTree T, Type k) ,68,例如:,二叉排序树,查找关键字,= 50 ,50,50,35 ,50,30,40,35,50,90 ,50,80,90,95,从上述查找过程可见,,在查找过程中,生成了一条查找路径:,从根结点出发,沿着左分支或右分支逐层向下直至关键字等于给定值的结点;,或者,从根结点出发,沿着左分支或右分支逐层向下直至指针指向空树为止。,查找成功,查找不成功,70,二叉排序树的查找性能: 从前述的两个查找例子(key100和key40)可见,在二又排序树上查找
30、其关键字等于给定值的结点的过程恰是走了一条从根结点到该结点的路径的过程,和给定值比较 的关键字个数等于路径长度加l(或结点所在层次数)。因此,和折半查找类似、与给定值比较的关键字个数不超过树的深度。,71,然而,折半查找长度为n的表的判定树是唯一的,而含有n个结点的二又排序树却不唯一。例如:下图中(a)和(b)的两棵二又排序树中结点的值都相同,但前者由关键字序到(45,2,53,12,37,93)构成而后者由关键字序列(12,24,37,45,53,93)构成。(a)树的深度为3,而(b)树的深度为6。再从平均查找长度来看,假设6个记录的查找概率相等,为16,,72,(a)关健字序列为(45,
31、24,53,12,37,93)的二叉排序序树;,45,24,53,12,37,93,则(a)树的平均查找长度为ASL(a)=(1+2+2+3+3+3)/6=14/6,73,(b)关键字序列为(12,24,37,45,53,93)的单支树。,12,24,37,45,53,93,而(b)树的平均查找长度为ASL(1十2十3+4十5+6)/621/6,74,因此,含有n个结点的二叉排序树的平均查找长度和树的形态有关。 当先后插入的关键字有序时,构成的二叉排序树蜕变为单支树。树的深度为n、其平均查找长度为(n+1)/2(和顺序查找相同),这是最差的情况。 显然,最好的情况是二叉排序树的形态和折半查找的
32、判定树相同,其平均查找长度和log2n成正比。,75,由关键字序列 3,1,2,5,4构造而得的二叉排序树,由关键字序列 1,2,3,4,5构造而得的二叉排序树,,例如:,2,1,3,4,5,3,5,4,1,2,ASL =(1+2+3+4+5)/ 5= 3,ASL =(1+2+3+2+3)/ 5 = 2.2,平均为:1+4log2n,根据动态查找表的定义,“插入”操作在查找不成功时才进行; 若二叉排序树为空树,则新插入的结点为新的根结点;否则,新插入的结点必为一个新的叶子结点,其插入位置由查找过程得到。,2 二叉排序树的插入算法,77,树的结构通常不是一次生成的,而是在查找过程中,当树中不存在
33、关键字等于给定值的结点时再进行插入。新插入的结点一定是一个新添加的叶子结点,并且是查找不成功时查找路径上访问的最后一个结点的左孩子或右孩子结点。,根据动态查找表的定义,“插入”操作在查找不成功时才进行;,若二叉排序树为空树,则新插入的结点为新的根结点;否则,新插入的结点必为一个新的叶子结点,其插入位置由查找过程得到。,插入,78,二叉排序树的插入操作之中包含着一个查找操作,该查找操作应具备两项功能:,确定要插入的关键字 key 二叉排序树中是否存在; 当查找不成功时返回插入位置 ( 即查找路径上最后一个结点的地址 ) 。,79,为此,需将上一小节的二叉排序树的查找算法改写成算 法5(b),以便
34、能在查找不成功时返回插入位置。插入算法如算法 6所示。,f 指向上一个查找过的结点。 算法中,若查找成功,f 指向找到结点的父结点;(父结点不存在时f = NULL)若查找不成功,f 指向查找路径上最后一个结点。(二叉排序树为空时f = NULL),80,BiTree SearchBST(BTree T,KeyType key,BiTree f,BiTree /查找结束else if(keydata.key)return SearchBST(T-lchild, key,T,p) /在左子树中继续查找elsereturn(SearchBST(T-rchild, key,T,p) /在右子树中继续
35、查找,81,查找45,T,f,1,BiTree SearchBST(BTree T,KeyType key,BiTree f,BiTree else if(keydata.key)return SearchBST(T-lchild, key,T,p); elsereturn(SearchBST(T-rchild, key,T,p);,53,17,78,65,97,09,81,45,23,82,53,17,78,65,97,09,81,45,23,查找45,T,f,2,BiTree SearchBST(BTree T,KeyType key,BiTree f,BiTree else if(key
36、data.key)return SearchBST(T-lchild, key,T,p); elsereturn(SearchBST(T-rchild, key,T,p);,83,53,17,78,65,97,09,81,45,23,查找45,T,f,3,p=T,BiTree SearchBST(BTree T,KeyType key,BiTree f,BiTree else if(keydata.key)return SearchBST(T-lchild, key,T,p); elsereturn(SearchBST(T-rchild, key,T,p);,84,53,17,78,65,97
37、,09,81,45,23,查找105,T,f=NULL,1,BiTree SearchBST(BTree T,KeyType key,BiTree f,BiTree else if(keydata.key)return SearchBST(T-lchild, key,T,p); elsereturn(SearchBST(T-rchild, key,T,p);,85,53,17,78,65,97,09,81,45,23,查找105,T,f,2,BiTree SearchBST(BTree T,KeyType key,BiTree f,BiTree else if(keydata.key)retu
38、rn SearchBST(T-lchild, key,T,p); elsereturn(SearchBST(T-rchild, key,T,p);,86,53,17,78,65,97,09,81,45,23,查找105,T,f,3,BiTree SearchBST(BTree T,KeyType key,BiTree f,BiTree else if(keydata.key)return SearchBST(T-lchild, key,T,p); elsereturn(SearchBST(T-rchild, key,T,p);,87,53,17,78,65,97,09,81,45,23,查找1
39、05,T,f,4,BiTree SearchBST(BTree T,KeyType key,BiTree f,BiTree else if(keydata.key)return SearchBST(T-lchild, key,T,p); elsereturn(SearchBST(T-rchild, key,T,p);,88,89,二叉排序树 的构建如何构造二叉排序树 构造过程: 从空树出发,依次插入各数据值: (1)如果二叉排序树是空树,则插入结点就是二叉排序树的根结点; (2)如果二叉排序树是非空的,则插入值与跟结点比较,若小于根结点的值,就插入到左子树中去;否则插入到右子树中。,90,vo
40、id InsertBST(BiTree /InsertBST,91,eg.设查找的关键字序列为45,24,53,45,12,24,90,45,45,24,45,24,53,45,24,53,12,45,24,53,12,90,92,容易看出,中序遍历二又排序树可得到一个关键字的有序序列这就是说,一个无序序列可以通过构造一棵二叉排序树而变成一个有序序列,构造树的过程即为对无序序列进行排序的过程。,93,不仅如此,从上面的插入过程还可以看到,每次插入的新结点都是二叉排序树上新的叶子结点,则在进行插入操作阶不必移动其它结点,仅需改动某个结点的指针,由空变为非空即可。 这就相当于在一个有序序列上插入一
41、个记录而不需要移动其它记录。它表明,二叉排序树既拥有类似于折半查找的特性,又采用了链表作存储结构,因此是动态查找表的一种适宜表示。,优点,94,输入数据,建立二叉排序树的过程,输入数据序列 53, 78, 65, 17, 87, 09, 81, 45, 23 ,53,17,78,65,87,09,81,45,23,95,53,53,78,53,17,78,65,53,17,78,65,87,65,53,78, 53, 78, 65, 17, 87, 09, 81, 45, 23 ,96,53,17,78,65,87,09, 53, 78, 65, 17, 87, 09, 81, 45, 23
42、,97,53,17,78,65,87,09,81, 53, 78, 65, 17, 87, 09, 81, 45, 23 ,98,53,17,78,65,97,09,81,45, 53, 78, 65, 17, 87, 09, 81, 45, 23 ,99,53,17,78,65,97,09,81,45,23, 53, 78, 65, 17, 87, 09, 81, 45, 23 ,100,同样3个数据 1, 2, 3 ,输入顺序不同,建立起来的二叉排序树的形态也不同。这直接影响到二叉排序树的查找性能。 如果输入序列选得不好,会建立起一棵单支树,使得二叉排序树的高度达到最大,这样必然会降低查找
43、性能。,101,1,2,3,1,1,1,1,3,2,2,2,3,3,2,3,2, 1, 3 1, 2, 3 1, 3, 2 2, 3, 1 3, 1, 2 3, 2, 1,102,103,删除,同样,在二又排序树上删去一个结点也很方便。 对于一般的二叉树来说,删去树中一结点是没有意义的。因为它将使以被删结点为根的子树成为森林,破坏了整裸树的结构。 然而,对于二叉排序树,删去树上一个结点相当于删去有序序列中的一个记录,只要删除某个结点之后依旧保持二又排序树的特性即可。 那末,如何在二又排序树上删去一个结点呢?,104,(1)被删除的结点是叶子; (2)被删除的结点只有左子树或者只有右子树; (3
44、)被删除的结点既有左子树,也有右子树。,可分三种情况讨论:,和插入相反,删除在查找成功之后进行,并且要求在删除二叉排序树上某个结点之后,仍然保持二叉排序树的特性。,105,(1)被删除的结点是叶子结点,例如:,被删关键字 = 20,88,其双亲结点中相应指针域的值改为“空”,106,(2)被删除的结点只有左子树或者只有右子树,其双亲结点的相应指针域的值改为 “指向被删除结点的左子树或右子树”。,被删关键字 = 40,80,107,(3)被删除的结点既有左子树,也有右子树,80,80,以其后继替代之,然后再删除该后继结点,被删结点,后继结点,被删关键字 =,50,108,(3)被删除的结点既有左
45、子树,也有右子树,40,40,以其前驱替代之,然后再删除该前驱结点,被删结点,前驱结点,被删关键字 = 50,109,假设在二叉排序树上被删结点为*p(指向结点的指针为p),其双亲结点为*f(结点指针为f),且不失一般性,可设*p是*f的左孩子(图 a 所示)。 下面分三种情况进行讨论: (1)若*p结点为叶子结点,即PL和PR均为空树。由于则去叶子结点不破坏整棵树的结构,则只得修改其双亲结点的指针即可。(2)若*p结点只有左子树PL或者只有有子树PR此时只要令PL或PR直接成为其双亲结点的左子树即可。显然,作此修改也不破坏二叉排序树的特性。,110,(3)若*p结点的左子树和右子树均不空。
46、显然,此时不能如上简单处理。 从图 b 可知,在删去*p结点之前,中序遍历该二叉树得到的序列为CLCQLQSLSPPRF,在删去*p之后,为保持其它元素之间的相对位置不变,可以有两种做法:,111,算法,Delete(BiTree ,112,else /左右子树均不空q = p; s = p-lchild;while (s-rchild)/找左子树上的最大的结点 q = s; s = s-rchild; p-data = s-data;/s指向被删结点的“后继”if (q != p) q-rchild = s-lchild;/重接*q的右子树else q-lchild = s-lchild;/重接*q的左子树delete s ; return TRUE; / Delete,113,if(!p-rchild) / 右子树为空树则只需重接它的左子树,