1、1,第5章 集合与查找,本章介绍第三种逻辑结构“集合”。 集合的实例: 一本词典中所有单词的集合; 图书馆内所有藏书的集合; 主要关注数据结构中 “集合”的实现方式以及相关操作(如:查找)等内容。,2,第5章 集合与查找,5.1 集合及其实现 5.2 具有Merge、Find的ADT集合 5.3 静态查找 5.4 哈希表 5.5 二叉查找树 5.6 平衡二叉查找树 5.7 B-树与B+树,3,第5章 集合与查找,5.1 集合及其实现 5.2 具有Merge、Find的ADT集合 5.3 静态查找 5.4 哈希表 5.5 二叉查找树 5.6 平衡二叉查找树 5.7 B-树与B+树,4,5.1 集
2、合及其实现,集合的基本概念 数学中的集合是无序集,并且要求集合中的成员互不相同。 “算法与数据结构”中的集合也要求其成员互不相同,且同一集合中所有成员具有相同的数据类型(如:整数、字符、字符串等),但成员间是有次序的。,name = “An”, “Cao”, “Liu”, “Ma”, “Peng”, “Wang”, “zhang” ,5,5.1 集合及其实现,集合的基本概念 集合的主要运算包括: 交 并 差 查找(指定元素是否在集合中),6,集合的ADT描述 ADT Set Data: 同属于一种结构的一组记录s1, s2, sn, 集合的成员互不相同。OperationsConstructo
3、r /构造Process: 初始化当前集合为空集Union /并集运算Input: 集合AProcess: 求当前集合与集合A的并集BOutput: 集合B,5.1 集合及其实现,7,Intersection /交集运算Input: 集合AProcess: 求当前集合与集合A的交集BOutput: 集合BDifferent /差集运算Input: 集合AProcess: 求当前集合与集合A的差集BOutput: 集合BAssignment /赋值运算Input: 集合AProcess: 将集合A赋值给当前集合,5.1 集合及其实现,8,Equality /等价性比较Input: 集合AProc
4、ess: 当前集合与集合A进行等价性比较Output: 若等价,返回true,否则返回falseMember /成员运算Input: 元素xProcess: 判断x是否属于当前集合Output: 若x属于当前集合返回true,否则falseInsert /插入运算Input: 元素xProcess: 将元素x插入当前集合中,5.1 集合及其实现,9,Delete /删除运算Input: 元素xProcess: 将元素x从当前集合中删除 /Set,5.1 集合及其实现,10,5.1 集合及其实现,集合的存储表示 链式存储有序链表 顺序存储位向量,11,5.1 集合及其实现,集合的存储表示 有序链
5、表 位向量,12,用带头结点的有序链表表示集合,first,first,8,17,23,35,49,72,用有序链表来表示集合时,链表中的每个结点表示集合的一个成员。 各结点所表示的成员 e0, e1, , en 在链表中按升序(或降序)排列,即 e0 e1 en。 有序链表表示的集合可以随意增加成员,因此,有序链表能描述无穷集合的任意子集。,13,5.1 集合及其实现,集合的存储表示 有序链表 位向量,14,用位向量实现集合,设 0, 1, 2, , n 是一个整数全集合。当n 是不大的整数时,可用位(0, 1)向量来实现全集合及其子集。, 1, 2, 3, 8, 9, ,其子集合 S1:
6、1, 2, 3, 8, 9 可表示为:,例如:全集合 S: 0, 1, 2, 3, 4, 5, 6, 7, 9, 10 可表示为:,15,当全集合不是由整数组成时,可建立非整数全集合成员与整数全集合成员 0, 1, 2, n的一一对应关系,并用位向量来表示该非整数全集合及其子集。,例如:非整数全集合 A, B, C, , X, Y, Z ,则可建立一个长度为26个单元的数组A ,规定数组的第i个单元Ai表示该集合的第i个成员,并且Ai取1表示第i个成员出现,取0表示不出现。, A, B, C, D, , , X, Y, Z ,用位向量实现集合,A =,16,当全集合不是由整数组成时,可建立非整
7、数全集合成员与整数全集合成员 0, 1, 2, n的一一对应关系,并用位向量来表示该非整数全集合及其子集。,例如:非整数全集合 A, B, C, , X, Y, Z ,则可建立一个长度为26个单元的数组A ,规定数组的第i个单元Ai表示该集合的第i个成员,并且Ai取1表示第i个成员出现,取0表示不出现。,用位向量实现集合,B C D I J,子集合S1: B, C, D, , I, J 对应的位向量为:,17,位向量实现的集合类定义如下:const int DefaultSize = 50; class BitSet int Maxsize; /集合的大小int *v; /存储集合元素的位向量
8、 public:BitSet ( int size = DefaultSize); /初始化为空集BitSet Union ( BitSet x); /并集运算BitSet Intersection ( BitSet x); /交集运算BitSet Different ( BitSet x); /差集运算,18,void Assignment ( BitSet x );/将集合x赋值给当前对象bool Equality ( BitSet x ); /等价性比较bool Member ( int x ); /判断元素x是否为成员void Insert ( int x ); /插入元素void D
9、elete ( int x ); /删除元素void Inset ( ); /输入集合void Outset ( ); /输出集合 ;,19,位向量定义的集合类中部分函数的实现:BitSet:BitSet (int size = DefaultSize) MaxSize = size; /集合的大小v = new intMaxSize;for (i=0; iMaxSize; i+)vi = 0; /初始化集合中的单元 /BitSet,0 0 0 0 0 0 0 0 0 0,0 9,20,位向量定义的集合类中部分函数的实现:void BitSet:Assignment (BitSet x) /将
10、集合x赋值给当前对象for (i=0; iMaxSize; i+)vi = x.vi; /初始化集合中的单元 /Assignment,this,0 1 1 1 0 0 0 0 1 1 0,x,0 1 1 1 0 0 0 0 1 1 0, 1, 2, 3, 8, 9 , 1, 2, 3, 8, 9 ,21,位向量定义的集合类中部分函数的实现:bool BitSet:Member (int x) /判断x是否为当前集合的成员if (x=MaxSize)return false;if (vx=1)return true;elsereturn false; /Member,22,位向量定义的集合类中部
11、分函数的实现:bool BitSet:Equality (BitSeT x) /等价性比较bool retval = true;if (MaxSize != x.MaxSize)return false; /成员数目不等,返回falsefor (i=0; iMaxSize; i+)if (vi != x.vi) retval = false; /对应成员不同,返回falsebreak;return retval; /Equality,23,位向量定义的集合类中部分函数的实现:BitSet BitSet:Union (BitSeT x) /并集运算BitSet tmp(MaxSize);for
12、(i=0; iMaxSize; i+)tmp.vi = vi | x.vi; /“按位或”运算return tmp; /Union,this,x,this,0 1 1 1 0 0 0 0 1 1 0,0 0 1 0 0 1 0 1 0 1 0,0 1 1 1 0 1 0 1 1 1 0, 1, 2, 3, 8, 9 , 2, 5, 7, 9 , 1, 2, 3, 5, 7, 8, 9 ,24,位向量定义的集合类中部分函数的实现:BitSet BitSet:Intersection (BitSeT x) /交集运算BitSet tmp(MaxSize);for (i=0; iMaxSize; i
13、+)tmp.vi = vi /Intersection,this,x,this,0 1 1 1 0 0 0 0 1 1 0,0 0 1 0 0 1 0 1 0 1 0,0 0 1 0 0 0 0 0 0 1 0, 1, 2, 3, 8, 9 , 2, 5, 7, 9 , 2, 9 ,25,位向量定义的集合类中部分函数的实现:BitSet BitSet:Different (BitSeT x) /差集运算BitSet tmp(MaxSize);for (i=0; iMaxSize; i+)tmp.vi = vi (vi /Different,this,x,this,0 1 1 1 0 0 0 0
14、 1 1 0,0 0 1 0 0 1 0 1 0 1 0,0 1 0 1 0 0 0 0 1 0 0, 1, 2, 3, 8, 9 , 2, 5, 7, 9 , 1, 3, 8 ,26,位向量定义的集合类中部分函数的实现:void BitSet:Insert (int x) /插入元素if (x=0 /Insert,this,0 0 0 0 0 0 0 0 0 0 0,0 9,1,27,位向量定义的集合类中部分函数的实现:void BitSet:Delete (int x) /删除元素if (x=0 /Delete,this,0 9,0,0 1 1 1 0 0 0 0 1 1 0,28,第5章
15、 集合与查找,5.1 集合及其实现 5.2 具有Merge、Find的ADT集合 5.3 静态查找 5.4 哈希表 5.5 二叉查找树 5.6 平衡二叉查找树 5.7 B-树与B+树,29,5.2 具有Merge、Find的ADT集合,在一些应用中,需要将一个集合划分成一组互不相交的子集。 例如: 将自然数集合按模n同余分组; 将某个班级的学生按性别分组。 一般过程如下: 初始时,每个元素构成一个集合; 然后,按一定规则(等价关系)将具有相同属性的集合合并(等价类); 直至不能合并为止。,30,5.2 具有Merge、Find的ADT集合,在一些应用中,需要将一个集合划分成一组互不相交的子集。
16、 这期间需要反复用到的操作是: (1)查找一个成员在哪个集合中; (2)将两个集合合并; 具有这两种集合操作的抽象数据类型被称为并查集(Merge & Find)。,31,5.2 具有Merge、Find的ADT集合,MFSet集合的ADT描述 ADT MFSet Data: 由n个元素构成的集合s。Operations:ConstructorInput: x1, x2, , xnProcess: 用x1, x2, , xn生成集合sMergeInput: a和b是s的两个子集Process: 将a和b中的一个并入另一个,32,5.2 具有Merge、Find的ADT集合,MFSet集合的AD
17、T描述FindInput: 元素eProcess: 确定e所属子集siOutput: 返回siOutProcess: 输出非空子集 /MFSet,33,5.2 具有Merge、Find的ADT集合,MFSet集合的实现 典型方法:采用“双亲表示法”的树形结构来表示MFSet。 约定: 以森林F = (T1,T2, , Tn)表示MFSet的全集,森林的每一棵树Ti(i1, 2, , n)代表MFSet的一个子集Si。森林的每一棵树Ti(i1, 2, , n)都采用“双亲表示法”,树中每个结点表示子集Si中的一个成员。,34,5.2 具有Merge、Find的ADT集合,MFSet集合的实现 典
18、型方法:采用“双亲表示法”的树形结构来表示MFSet。 例如:s1=0,6,7,8; s2=1,4,9; s3=2,3,5。,35,5.2 具有Merge、Find的ADT集合,MFSet集合的实现 典型方法:采用“双亲表示法”的树形结构来表示MFSet。 好处: Merge操作很容易实现:将一棵树的根指向另一棵树即可。,S1,S2,S1S2,36,5.2 具有Merge、Find的ADT集合,MFSet集合的实现 典型方法:采用“双亲表示法”的树形结构来表示MFSet。 好处: Find操作也很容易实现:顺着结点的parent域找到树的根结点即可查找到该结点所属子集。,37,5.2 具有Me
19、rge、Find的ADT集合,MFSet集合采用双亲表示法的实现 class MFSet /并查集类的定义int n; /集合中成员个数int *parent; /下标对应成员,值为双亲,根的双亲置为-1public:MFSet( int m );void Merge( int a, int b ); /a和b为两个子集的根int Find( int e ); /查找成员e所属子集,返回根的下标void Out( ); /输出并查集 ; /MFSet,38,5.2 具有Merge、Find的ADT集合,MFSet集合采用双亲表示法的实现 MFSet:MFSet( int m ) /初始化函数,
20、每个成员单独构成一个集合n = m;parent = new intn;for(i=0; in; i+)parenti = -1; /初始时,每个成员都是一棵树 /MFSet,39,5.2 具有Merge、Find的ADT集合,MFSet集合采用双亲表示法的实现 void MFSet:Merge( int a, int b ) /a, b为两个子集的根parentb = a; /集合的“并”操作,b并入a /Merge,40,5.2 具有Merge、Find的ADT集合,MFSet集合采用双亲表示法的实现 int MFSet:Find( int e) /集合的“查找”操作if (e=n)ret
21、urn -1;while(parente!=-1)e = parente;return e; /Find,41,5.2 具有Merge、Find的ADT集合,假设n个成员的全集合,Merge和Find的性能分析: 执行Merge操作的时间是O(1)。 执行Find(e)操作的时间与Merge的策略有关: 最好情况:O(1) 最坏情况:O(e),42,5.2 具有Merge、Find的ADT集合,假设最初n个元素构成n棵树组成的森林,依次执行Merge(n-2, n-1),Merge(1, 2),Merge(0, 1),将产生如下图所示的树:,执行一次Merge操作所需时间是O(1),n-1次M
22、erge操作所需时间是O(n)。 若再执行Find(0),Find(1),Find(n-1),若被搜索的元素为i,完成Find(i)操作需要时间为O(i),完成n次搜索需要的总时间将达到:,43,5.2 具有Merge、Find的ADT集合,改进:(目标:降低树的高度) 在Merge算法中,采用带权归并技术。 基本思想:将树中的结点数作为树的权值,在合并两个树的过程中,将权值小的树根指向权值大的树根。 为此,需要修改存储结构:令根结点parent域保存树中所含结点数目的负值。 在Find算法中,采用路径压缩技术。 基本思想:缩短从结点e到根的路径长度。 将结点e到根的路径上的所有结点都指向根结
23、点,从而实现路径压缩。,44,5.2 具有Merge、Find的ADT集合,Merge改进算法 void MFSet:Merge( int a, int b ) /a, b为两个子集的根if (-parenta-parentb) /a小,a并入bparentb += parenta;parenta = b;else /b小,b并入aparenta += parentb;parentb = a; /Merge,45,5.2 具有Merge、Find的ADT集合,Merge改进算法,46,5.2 具有Merge、Find的ADT集合,Find改进算法 int MFSet:Find( int e)
24、/通过压缩路径改进Find算法/确定e所在子集,并将从e至根路径上所有结点都变成根的孩子结点。if (e=n)return -1;j=e;while(parentj=0) / 找元素j的根j=parentj;for( ; e!=j; e=temp ) /j是e的双亲 temp=parente; parente=j; return j; /返回e的双亲 /Find,47,5.2 具有Merge、Find的ADT集合,Find改进算法,48,5.2 具有Merge、Find的ADT集合,假设n个成员的全集合,改进Merge和Find的性能分析: 执行改进Merge操作的时间比原来略有增加,但时间复
25、杂度仍为O(1)。 结论:从单个成员出发,执行n-1次改进Merge操作,构建含n个结点的树t,该树的最大高度为log2(n)+1。,49,5.2 具有Merge、Find的ADT集合,假设n个成员的全集合,改进Merge和Find的性能分析: 执行改进Find(e)操作,其搜索时间的上限不超过树的高度。 最好情况:O(1) 最坏情况:O(log2(n),50,第5章 集合与查找,5.1 集合及其实现 5.2 具有Merge、Find的ADT集合 5.3 静态查找 5.4 哈希表 5.5 二叉查找树 5.6 平衡二叉查找树 5.7 B-树与B+树,51,若表中存在特定元素,称查找成功,应输出该
26、元素。 否则,称查找不成功(也应输出失败标志)。,查找表 查 找 查找成功 查找不成功 静态查找 动态查找 关键字 主关键字 次关键字,由同一类型的数据元素(或记录)构成的集合。,查询 (Searching) 特定元素是否在表中。,只查找,不改变集合内的数据元素。 既查找,又改变(增、减)集合内的数据元素。 元素中某个数据项的值,可用来识别一个元素。可以唯一标识一个元素的关键字。,例如“学号”,例如“姓名”,识别若干元素的关键字。,第5章 集合与查找,52,第5章 集合与查找,查找的过程是怎样进行的? 给定一个值K,在含有n个元素的查找表中寻找一个关键字值等于K的元素,如找到则输出该元素或者该
27、元素在表中的位置,否则输出查找不成功的信息。,对查找表常用的操作有哪些? 查询某个“特定”数据元素是否在表中; 在查找表中插入一个元素; 在查找表中删除一个元素;,静态查找,53,第5章 集合与查找,如何评估查找算法的优劣?,查找的过程就是将给定的K值与查找表中各元素的关键字进行比较的过程。 常用比较次数的平均值来评估查找算法的优劣平均查找长度(Average Search Length, ASL)。,54,显然,ASL值越小,时间效率越高。,设查找第i个元素的概率为pi ,查找到第i个元素所需比较次数为ci,则查找成功的平均查找长度:,第5章 集合与查找,55,“查找”所关注的焦点问题:,1
28、. 如何组织松散的结构,以便提高查找效率; 顺序表、链表; 哈希表; 二叉查找树; 2. 如何对查找表进行操作,并分析操作效率。,第5章 集合与查找,56,第5章 集合与查找,5.1 集合及其实现 5.2 具有Merge、Find的ADT集合 5.3 静态查找 5.4 哈希表 5.5 二叉查找树 5.6 平衡二叉查找树 5.7 B-树与B+树,57,5.3 静态查找,静态查找:只对查找表进行查找操作,而不进行插入或者删除操作。 主要方法包括: 顺序查找 二分查找(折半查找) 索引顺序查找,58,顺序查找 (Sequential Search),所谓顺序查找,又称线性查找,主要用于在线性结构(如
29、:线性表)中进行查找。 顺序查找是从查找表的表尾(或表头)开始,顺序用各对象的关键码与给定值x进行比较,直到找到与其值相等的对象,则查找成功,给出该对象在表中的位置。 若整个查找表都已检测完仍未找到关键码与x相等的对象,则查找失败,给出失败信息。,59,顺序查找 (Sequential Search),60,顺序查找的平均查找长度,设查找第 i 个元素的概率为pi,查找到第 i 个元素所需比较次数为ci, 则查找成功的平均查找长度:,在顺序查找中,查找到第 i 个元素的比较次数ci = n-i, i = 0, 1, , n-1,因此,,61,在等概率情况下,pi = 1/n, i = 1, 2
30、, , n:,查找不成功的平均查找长度,ASLunsuccess = n,顺序查找的优、缺点:优点:对查找表中的元素无特殊要求(无需排序),采用顺序或者链式存储均可;缺点: 平均查找长度较大。,顺序查找的平均查找长度,62,查找思想:先确定待查找记录所在的范围,然后逐步缩小范围,直到找到或找不到该记录为止。 前提条件:必须在具有顺序存储结构的有序(递增或者递减)线性表中进行。,二分查找,63,二分查找,设n个对象存放在一个有序顺序表中,并按其关键码递增(或递减)排序。 二分查找时,先求位于查找区间正中对象的下标mid,用其关键码与给定值x比较:Elementmid.key = x,查找成功;E
31、lementmid.key x,把查找区间缩小到表的前半部分,继续二分查找;Elementmid.key x,把查找区间缩小到表的后半部分,继续二分查找。 如果查找区间已缩小到一个对象,且仍未找到想要的查找对象,则查找失败。,64,-1 0 1 3 4 6 8 10 12,6,0 1 2 3 4 5 6 7 8,搜索,low,mid,high,6,65,-1 0 1 3 4 6 8 10 12,5,0 1 2 3 4 5 6 7 8,搜索,low,mid,high,5,66,int BinarySearch ( const DataType x ) /二分查找算法int high = MaxS
32、ize-1, low = 0, mid; while ( low x )high = mid - 1; /左缩查找区间else return mid; /查找成功return -1; /查找失败 ,67,二分查找算法分析: orderT : 0 1 2 3 4 5 6 7 8 9 10 11 12 13,2 4 6 8 10 13 15 18 20 22 24 25 27 30,15,6,2,4,10,8,13,24,20,27,18,22,25,30,二分查找的判定树:比较次数不超过完全二叉树高度。,68,最坏情况下的查找长度是: 二分查找成功的平均查找长度:设查找任一元素的概率都相等,即:
33、pi=1/n在第k层上最多有2k-1 个结点,在第k层上的结点需比较k次。,69,可以用归纳法证明:,这样,,70,二分查找的优点、缺点: 优点:平均查找长度与最大查找长度近似(都不超过完全二叉树的高度),检索速度较快。 缺点:只适合顺序存储、需要排序。,二分查找,71,1. 概念:是顺序查找的一种改进。 索引项:(key, addr), 其中:key是关键字,addr是地址。 索引表:由索引项构成的顺序表。,如何查找关键字为60的元素?,索引顺序查找(分块查找),72,2. 分块查找的算法思想: D=d1,d2,.,dn 1) 将数据集分为s个块B1, B2, , Bs; 2) 块间有序:当
34、ij时,Bi中的任一元素小于Bj中的任 一元素; 3) 为每个Bi块设一索引项(keyi, addri); 4) s个索引项构成索引表; 5) 块内可有序,也可无序;,索引顺序查找(分块查找),73,3. 查找过程: (1)首先在索引表中查找,确定在哪个块中。可以是二分查找,也可以是顺序查找。 (2)在块中顺序查找。,索引顺序查找(分块查找),74,4. 算法分析: 设在索引表中和块内都是顺序查找。(s是索引项的数目) 索引表中查找:ASLindex=(s+1)/2 块内查找:ASLblock=(n/s+1)/2 所以:ASLbs= (s+1)/2 + (n/s+1)/2 显然它与s 的取值有
35、关。 当 时,ASL有最小值 。,例如:当n=28,s=24时,ASLbs=17。,索引顺序查找(分块查找),75,索引顺序查找优点、缺点: 优点:块内无需排序,且顺序存储或者链式存储均可。 缺点:增加一个辅助数组的存储空间,并且该数组须按分块排序。,索引顺序查找(分块查找),76,顺序查找、二分查找、索引顺序查找性能分析: 当n=10,000时: 顺序查找的比较次数:5,000((n+1)/2) 二分查找的比较次数:14(log2n) 索引顺序的比较次数:100(s=100),5.3 静态查找,77,第5章 集合与查找,5.1 集合及其实现 5.2 具有Merge、Find的ADT集合 5.
36、3 静态查找 5.4 哈希表 5.5 二叉查找树 5.6 平衡二叉查找树 5.7 B-树与B+树,78,前面介绍的静态查找算法(如:顺序查找、二分查找、索引顺序查找等),查找表中数据元素的存储位置与其关键字之间不存在直接对应关系,因此在查找表中查找某个关键字时,需要经过一系列“比较”操作,而且查找的效率依赖于查找过程中的比较次数。,哈希表 (Hashing),79,想法:能否不经过“比较”,直接就能找到具有相应关键字的数据元素? 需要在数据元素的“存储位置”与其“关键字”之间建立一个一一对应的关系。,哈希表 (Hashing),80,例如:为每年招收的1000名新生建立一个查找表,其关键字为x
37、x000 xx999(前两位为年份)。 可采用下标为0 999的顺序表来描述该查找表。 由于关键字的后三位和数据元素在查找表中的序号相同,因此,不经过“比较”即可确定待查关键字的存储位置。,哈希表 (Hashing),81,哈希表(Hashing),又称散列表。哈希表的基本思想:以查找表中的每个数据元素的关键字k为自变量,通过一种函数H(k)计算出相应函数值,该值作为数据元素在查找表中的存储位置。 函数H称为哈希函数或散列函数。 按这种方法建立的查找表称为哈希表或散列表。,哈希表 (Hashing),82,哈希表的相关内容: 哈希函数:用于构建哈希表以及查找。什么样的函数可作为哈希函数? 处理
38、冲突:当多个数据元素的通过哈希函数得到的存储位置相同时(发生冲突),需要处理冲突。处理冲突的方法有哪些?,哈希表 (Hashing),83,哈希表的相关内容: 哈希函数 处理冲突的方法 哈希表的查找及性能分析,哈希表 (Hashing),84,哈希表的相关内容: 哈希函数 处理冲突的方法 哈希表的查找及性能分析,哈希表 (Hashing),85,哈希函数的设计原则: 使计算出的存储地址,尽可能均匀地分布在整个地址空间中,即:应尽可能减少冲突的发生。 为了提高关键字到存储地址的转换(计算)速度,哈希函数应该越简单越好。 根据上述原则,下面介绍六种常用的哈希函数构造方法。,哈希函数,86,直接地址
39、法 此类函数取关键字的某个线性函数值作为哈希地址:Hash ( key ) a * key + b 其中:a,b为常数。,哈希函数,87,例:有如下一组关键字 942148, 941269, 940527, 941630, 941805, 941558, 942047, 940001 。 哈希函数为:Hash (key) = key - 940000 Hash (942148) = 2148 Hash (941269) = 1269 Hash (940527) = 527 Hash (941630) = 1630 Hash (941805) = 1805 Hash (941558) = 155
40、8 Hash (942047) = 2047 Hash (940001) = 1 假设地址空间能容纳上述计算出来的哈希地址。,哈希函数,88,直接地址法 优点:这种哈希函数是一对一的映射,一般不会产生冲突。 缺点:它要求哈希地址空间的大小与关键字集合的大小相同,而通常无法事先知道关键字的范围。,哈希函数,89,折叠法 将关键字从左至右分成位数相等的几部分,除了最后一部分的位数可以短一些之外,其他每部分的位数应与哈希表中地址的位数相同。 这些部分的叠加和(舍去无效的高位),就是该关键字的哈希地址。,哈希函数,90,例:设给定的关键字key = 23938587841,若存储地址限定3位,则划分结
41、果为每段3位。 上述关键字从左至右可划分为以下4段:239 385 878 41,哈希函数,哈希地址:543,91,折叠法的适用情况:当关键字的位数很多,而且在关键字中每一位上的数字分布大致均匀时,可用这种方法得到哈希地址。,哈希函数,92,数字分析法 设有n个d位数,每一位可能有 r 种不同的符号,且这 r 种不同符号在各位上出现的频率(概率)不一定相同。 根据哈希表的大小,选取其中各种符号分布均匀的若干位作为哈希地址。,哈希函数,93,数字分析法 计算各位数字中符号分布均匀度 k的公式:其中: 表示第 i 个符号在第 k 位上出现的次数,n/r 表示各种符号在 n 个数中均匀出现的期望值。
42、 说明: k 的值越小,表明在该位 (第k 位) 各种符号分布越均匀。,哈希函数,94,假设哈希表地址范围有3位数字,取各关键字的第、位作为数据元素的哈希地址。,哈希函数,95,缺点:哈希函数依赖于关键字集合,需要事先知道可能出现的关键字。,哈希函数,96,基数转换法 将关键字看成另一个进制上的数,然后将它转换成原来进制的数,再用数字分析法取其中的几位作为地址。 例如:key236075(236075)132135313461337135(841547)10按数字分析,选2、3、4、5位作为地址,则h(236075)=4154。,哈希函数,97,平方取中法 基本思想:取关键字平方后的中间几位作
43、为哈希地址(超出范围时,可再取模)。 例:设有一组关键字ABC,BCD,CDE,DEF,每个字母有其对应的机内码。假定存储空间的地址大小为1000,编号为0999。,哈希函数,98,平方取中法 可取关键字机内码平方后的中间三位作为存储地址(哈希地址)。,哈希函数,99,除留余数法(除余法) 设哈希表中允许的地址个数为m,取一个不大于m的素数p作为除数,利用以下函数将关键字转换成哈希地址:hash ( key ) = key % p (p m),哈希函数,100,除留余数法(除余法) 例:有一个关键字key = 962148,哈希表大小 m = 25,取质数 p= 23,哈希函数为 hash (
44、 key ) = key % p,则该关键字的哈希地址为:hash ( 962148 ) = 962148 % 23 = 12。 说明:使用上面的哈希函数计算出来的地址范围是022,因此,地址23和24不可能用哈希函数计算出来,只有在处理冲突时才能使用这些地址。,哈希函数,101,小结: 以上介绍了几种常用的构造哈希函数的方法,在实际中,应根据关键字的特点,选用适当的方法。 除留余数法是最常用的构造哈希函数的方法。 有人用统计分析方法对这几种哈希函数进行了模拟分析,结论是平方取中法最接近于“随机化”。,哈希函数,102,哈希表的相关内容: 哈希函数 处理冲突的方法 哈希表的查找及性能分析,哈希
45、表 (Hashing),103,冲突:假设哈希表的地址范围为0m-l,当对给定的关键字k,由哈希函数H(k)计算出的哈希地址为i(0im-1),如果该位置上已存有记录,则称这种现象为“冲突”。 处理冲突:就是为发生冲突的关键字找到另一个“空”的哈希地址,即:通过一个新的哈希函数得到一个新的哈希地址,如果仍然发生冲突,则再求下一个,直至新的哈希地址不再发生冲突为止。,处理冲突的方法,104,常用的处理冲突的两种方法: 闭哈希法(开放地址法) 不开辟新空间,也不改变哈希表的大小,寻找的新地址就在原哈希表的地址范围内。 开哈希法(单链法) 另外开辟新空间,但不改变哈希表的大小,发生冲突的记录存放到另
46、外开辟的空间中。,处理冲突的方法,105,闭哈希法,基本思想:当冲突发生时,形成一个地址序列,沿着这个序列逐个探测,直到找出一个“空”的地址,将发生冲突的关键字存放到该地址。 常用的探测方法: 线性探测法 随机探测法 二次探测法 再哈希法,106,(1)线性探测法 从发生冲突的地址(设为d)开始,依次探测地址d+l,d+2,m-1(当达到表尾m-1时,又从0开始探查),直到找到一个“空地址”来存放发生冲突的关键字。 若整个哈希表都找遍仍无空地址,则产生溢出。,闭哈希法线性探测法,107,(1)线性探测法 线性探测法的递推公式为:d0 = H(key)di = (di-1+1) % m (1im-1),闭哈希法线性探测法,108,例: 关键字集合 19, 01, 23, 14, 55, 68, 11, 82, 36 ,设定哈希函数 H(key) = key % 11 ( 表长=11 ),采用线性探测法来处理冲突。,19,