数据结构 第七章 查找 习题

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

7.2 顺序查找和折半查找

顺序查找

一般线性表

从线性表的一端开始,逐个检查关键字是否满足给定的条件。若查找到某个元素的关键字满足给定条件,则查找成功,返回该元素在线性表中的位置:若己经查找到表的另一端,但还没有查找到符合给定条件的元素,则返回查找失败的信息。

typedef struct{
	ELemType *elem;
	int TableLen;
}SSTable;

int Search_Seq(SSTable ST, ElemType key){
	ST.elem[0]=key;	//哨兵
	for(i=ST.TableLen;ST.elem[i]!=key;--i)
		return i;
}

哨兵可以使得程序不用判断数组是否越界。

一般线性表顺序查找的平均长度为 A S L 成 功 = ∑ i = 1 n P ( n − i + 1 ) ASL_{成功}=\sum_{i=1}^{n}P(n-i+1) ASL=i=1nP(ni+1)
当每个元素查找概率相等时 A S L 成 功 = ∑ i = 1 n P ( n − i + 1 ) = n + 1 2 ASL_{成功}=\sum_{i=1}^{n}P(n-i+1)=\frac{n+1}{2} ASL=i=1nP(ni+1)=2n+1
查找不成功时,平均长度为 A S L 不 成 功 = n + 1 ASL_{不成功}=n+1 ASL=n+1

优点
1)对数据存储没有要求;
2)对记录有序性没有要求。

缺点
1)当n较大时,平均查找长度较大,效率低。

有序表

若在查找之前就己经知道表是关键字有序的,则查找失败时可以不用再比较到表的另一端就能返回查找失败的信息,从而降低顺序查找失败的平均查找长度。
假设表 L L L是按关键字从小到大排列的,查找的顺序是从前往后,待查找元素的关键字为 k e y key key,当查找到第 i i i个元素时,发现第 i i i个元素对应的关键字小于 k e y key key但第 i + 1 i+1 i+1个元素对应的关键字大于 k e y key key,这时就可返回查找失败的信息,因为第 i i i个元素之后的元素的关键字均大于 k e y key key,所以表中不存在关键字为 k e y key key的元素。

有序表顺序查找不成功的平均长度为 A S L 不 成 功 = ∑ j = 1 n q j ( l j − 1 ) = 1 + 2 + . . . + n + n n + 1 = n 2 + n n + 1 ASL_{不成功}=\sum_{j=1}^{n}q_j(l_j-1)=\frac{1+2+...+n+n}{n+1}=\frac{n}{2}+\frac{n}{n+1} ASL=j=1nqj(lj1)=n+11+2+...+n+n=2n+n+1n,式中, q j q_j qj是到达第 j j j个失败结点的概率。
※式中 1 + 2 + . . . + n + n n + 1 \frac{1+2+...+n+n}{n+1} n+11+2+...+n+n是因为无论落在最后两个数据的区间内还是大于最后一个数据,查找长度都为 n n n

折半查找

又称二分查找,仅适用于有序的顺序表。
折半查找的基本思想:首先将给定值 k e y key key与表中中间位置的元素比较,若相等,则查找成功,
返回该元素的存储位置;若不等,则所需查找的元素只能在中间元素以外的前半部分或后半部分
(例如,在查找表升序排列时,若给定值key大于中间元素.则所查找的元素只可能在后半部分)。然后在缩小的范围内继续进行同样的查找,如此重复,直到找到为止,或确定表中没有所需要查找的元素,则查找不成功,返回查找失败的信息。算法如下:

int Binary_Search(SeqList L, ElemType key){
	int low=0, high=L.TableLen-1, mid;
	while(low<=high){
		mid=(high+low)/2;
		if(L.elem[mid]==k)
			return mid;
		else if(L.elem[mid]>key)
			high=mid-1;
		else
			low=mid+1;
	}
	return -1;	//查找失败
}

折半查找可用称为判定树的二叉树描述。
圆形结点:查找表一个记录,结点中的值为该记录的关键字值;
方形叶结点:表示查找不成功的情况。
查找成功时的查找长度为从根结点到目的结点的路径上的结点数;
杳找不成功时的查找长度为从根结点到对应失败结点的父结点的路径上的结点数;
每个结点值均大于其左子结点值,且均小于于其右子结点值。
若有序序列有 n n n个元素,则对应的判定树有 n n n个圆形的非叶结点和 n + 1 n+1 n+1个方形的叶结点。
显然,判定树是一棵平衡二叉树。

折半查找判定树
查找成功的平均长度:
A S L = 1 n ∑ i = 1 n l i = 1 n ( 1 × 1 + 2 × 2 + . . . + h × 2 h − 1 ) = n + 1 n l o g 2 ( n + 1 ) ≈ l o g 2 ( n + 1 ) − 1 ASL=\frac{1}{n}\sum_{i=1}^nl_i=\frac{1}{n}(1×1+2×2+...+h×2^{h-1})=\frac{n+1}{n}log_2^{(n+1)}≈log_2^(n+1)-1 ASL=n1i=1nli=n1(1×1+2×2+...+h×2h1)=nn+1log2(n+1)log2(n+1)1
其中 h h h是树的高度,并且元素个数为 n n n时树高 h = ⌈ l o g 2 ( n + 1 ) ⌉ h=\lceil log_2^{(n+1)}\rceil h=log2(n+1)
时间复杂度为 O ( l o g 2 n ) O(log_2^n) O(log2n),比较次数最多不会超过树的高度。

优点
1)效率高于顺序查找

缺点
1)该方法仅适合于顺序存储结构,不适合于链式存储结构
2)要求关键字有序

分块查找

分块查找又称索引順序查找。它吸取了顺序查找和折半查找各自的优点,既有动态结构,又
适于快速查找。
分块查找的基本思想将查找表分为若十子块。块内的元素可以无序,但块之间是有序的,即第一个块中的最大关键字小于第二个块中的所有记录的关键字,第二个块中的最大关键字小于
第三个块中的所有记录的关键字,以此类推。再建立一个索引表,索引表中的每个元素含有各块
的最大关腱字和各块中的第一个元素的地址,索引表按关键字有序排列。

分块查找的过程分为两步:
1)在索引表中确定待查记录所在的块,可以顺序查找或折半查找索引表;
2)在块内顺序查找。

分块查找示意图
其平均查找长度为 A S L = L I + L S ASL=L_I+L_S ASL=LI+LS
将长度为 n n n的查找表均匀分为 b b b块时,每块有 s s s个记录,在等概率情况下,
若在块内和索引表中均采用顺序查找,则其平均查找长度为 A S L = L I + L S = b + 1 2 + s + 1 2 = s 2 + 2 s + n 2 s ASL=L_I+L_S=\frac{b+1}{2}+\frac{s+1}{2}=\frac{s^2+2s+n}{2s} ASL=LI+LS=2b+1+2s+1=2ss2+2s+n$
若对索引表采用折半查找,则平均查找长度为 A S L = L I + L S = ⌈ l o g 2 ( n + 1 ) ⌉ + s + 1 2 ASL=L_I+L_S=\lceil log_2^{(n+1)}\rceil+\frac{s+1}{2} ASL=LI+LS=log2(n+1)+2s+1

习题7.2

选择题

  1. 当二叉排序树为单支树时,其查找长度为 O ( n ) O(n) O(n)(Q9)
  2. 对有 n n n个记录的索引表分块查找时,最理想的块长是 n \sqrt{n} n

简答题

7.2.1
1)查找失败
有序表:遇见第一个大于关键字的值即停止,平均查找失败长度为 A S L 失 败 = 1 + 2 + . . . + n 2 = n + 1 2 ASL_{失败}=\frac{1+2+...+n}{2}=\frac{n+1}{2} ASL=21+2+...+n=2n+1
无序表: A S L 失 败 = n ASL_{失败}=n ASL=n
2)查找成功,且表中只有一个关键字给定值k的元素。
相同,均为 A S L 成 功 = ∑ i = 1 n P ( n − i + 1 ) = n + 1 2 ASL_{成功}=\sum_{i=1}^{n}P(n-i+1)=\frac{n+1}{2} ASL=i=1nP(ni+1)=2n+1
3)查找成功,且表中有若干关键字等于给定值k的元素,要求一次查找能找出所有元素。
有序表:只要找到第一个相同就可以连续找到其他关键字相同的元素。
无序表:必须查找表中全部的元素才能找出相同关键字的元素。
7.2.2
3) A S L 成 功 = 1 + 2 ∗ 2 + 3 ∗ 4 + 4 ∗ 7 14 = 45 14 ASL_{成功}=\frac{1+2*2+3*4+4*7}{14}=\frac{45}{14} ASL=141+22+34+47=1445
A S L 不 成 功 = 3 ∗ 1 + 4 ∗ 14 15 = 59 15 ASL_{不成功}=\frac{3*1+4*14}{15}=\frac{59}{15} ASL=1531+414=1559
7.2.3
可以用k叉树来描述,k分查找发在查找成功时进行比较的关键字的个数最多不超过树的深度,而具有n个结点的k叉树的深度为 ⌊ l o g k n ⌋ + 1 \lfloor log_k^n\rfloor+1 logkn+1 k k k分查找法在查找成功时和给定的值进行比较的关键字个数至多为 ⌊ l o g k n ⌋ + 1 \lfloor log_k^n\rfloor+1 logkn+1,即时间复杂度为 O ( l o g k n ) O(log_k^n) O(logkn),同理,查找不成功时,和给定的值进行比较的关键字个数也至多为 ⌊ l o g k n ⌋ + 1 \lfloor log_k^n\rfloor+1 logkn+1,时间复杂度也为 O ( l o g k n ) O(log_k^n) O(logkn)
7.2.4
1)如图,其中每个关键字下的数字为其查找成功时的关键字比较次数。
7.2.4(1)
2)等查找概率下,平均每个关键字查找成功的概率为1/8n
A S L 成 功 = 1 8 n ∑ i = 0 8 n − 1 C i = 1 8 n ( ∑ i = 1 n i + ∑ i = 2 n + 1 i + ∑ i = 3 n + 2 i + ∑ i = 4 n + 3 i ) = 1 8 n ( ∑ i = 1 n ( i + ( i + 1 ) + 2 ( i + 2 ) + 4 ( i + 3 ) ) = 1 8 n ∑ i = 1 n ( 8 i + 17 ) = 1 n ∑ i = 1 n i + 17 8 = n + 1 2 + 17 8 ASL_{成功}=\frac{1}{8n}\sum_{i=0}^{8n-1}C_i=\frac{1}{8n}(\sum_{i=1}^ni+\sum_{i=2}^{n+1}i+\sum_{i=3}^{n+2}i+\sum_{i=4}^{n+3}i)=\frac{1}{8n}(\sum_{i=1}{n}(i+(i+1)+2(i+2)+4(i+3))=\frac{1}{8n}\sum_{i=1}^n(8i+17)=\frac{1}{n}\sum_{i=1}^ni+\frac{17}{8}=\frac{n+1}{2}+\frac{17}{8} ASL=8n1i=08n1Ci=8n1(i=1ni+i=2n+1i+i=3n+2i+i=4n+3i)=8n1(i=1n(i+(i+1)+2(i+2)+4(i+3))=8n1i=1n(8i+17)=n1i=1ni+817=2n+1+817

7.2.5
1)元素按其查找概率降序排列时查找长度最小。顺序查找。 A S L = 0.35 ∗ 1 + 035 ∗ 2 + 0.15 ∗ 3 + 0.15 ∗ 4 = 2.1 ASL=0.35*1+035*2+0.15*3+0.15*4=2.1 ASL=0.351+0352+0.153+0.154=2.1
2)同(1)顺序查找,或者用二叉排序树(按首字母先后顺序)
A S L = 0.15 ∗ 1 + 0.35 ∗ 2 + 0.35 ∗ 2 + 0.15 ∗ 3 = 2.0 ASL=0.15*1+0.35*2+0.35*2+0.15*3=2.0 ASL=0.151+0.352+0.352+0.153=2.0
二叉排序树

7.2.6

int BinSeacrhRec(SSTable ST, ElemType key, int low, int high){
	if(low>high)
		return 0;
	mid=(high+low)/2;
	if(key>ST.elem[mid])
		BinSeacrhRec(ST,key,mid+1,high);
	else if(key<ST.elem[mid])
		BinSeacrhRec(ST,key,low,mid-1);
	else 
		return mid;
}

7.2.7

int SeqSrch(RcdType R[], ElemType k){
	int i=0;
	while((R[i].key!=k)&&(i<n))
		i++; //找到k
	if(i<n&&i>0){	//将k往前移
		temp=R[i];
		R[i]=R[i-1];
		R[i-1]=temp;
	}
	else
		return -1;
}

7.3 B树和B+树

B树及其基本操作

B树,又称多路平衡查找树,B树中所有结点的孩子个数的最大值称为B树的阶,通常用m
表示。一棵m阶B树或为空树,或为满足如下特性的m叉树:
1)树中每个结点至多有m棵子树,即至多含有m-1个关键字。
2)若根结点不是终端结点,则至少有两棵子树。
3)除根结点外的所有非叶结点至少有 ⌈ m / 2 ⌉ \lceil m/2\rceil m/2棵子树,即至少含有 ⌈ m / 2 ⌉ − 1 \lceil m/2\rceil-1 m/21个关键字。
4)所有非叶结点的结构如下:其中 K i K_i Ki为结点的关键字,且满足升序, P i P_i Pi为指向子树根结点的指针,且 P i − 1 P_{i-1} Pi1所指子树中所有结点的关键字均小于 K i K_i Ki P i P_i Pi的关键字均大于 K i K_i Ki. n ( ⌈ m / 2 ⌉ − 1 ≤ n ≤ m − 1 ) n(\lceil m/2\rceil-1≤n≤m-1) n(m/21nm1)为结点中关键字的个数。
B树非叶结点
5)所有的叶结点都出现在同一层次上,并且不带信息(可以视为外部结点或类似于折半查
找判定树的查找失败结点,实际上这些结点不存在,指向这些结点的指针为空)。
B树是所有结点的平衡因子均等于0的多路平衡查找树。

B树的高度(磁盘存取次数)

讨论不包括不带信息的叶结点层。
n ≥ 1 n≥1 n1,则对任意一棵包含 n n n个关键字、高度为 h h h、阶数为m的B树:
1)因为B树中每个结点最多有 m m m棵子树, m − 1 m-1 m1个关键字,所以在一棵高度为 h h h m m m阶B树中关键字的个数应满足 n ≤ ( m − 1 ) ( 1 + m + m 2 + . . . + m h − 1 = m h − 1 n≤(m-1)(1+m+m^2+...+m^{h-1}=m^h-1 n(m1)(1+m+m2+...+mh1=mh1,因此有 h > l o g m ( n + 1 ) h>log_m^{(n+1)} h>logm(n+1).
2)若让每个结点中的关键字个数达到最少,则容纳同样多关键字的B树的高度达到最大。由B树的定义:第一层至少有1个结点:第二层至少有2个结点:除根结点外的每个非终端结点至少有 ⌈ m / 2 ⌉ \lceil m/2\rceil m/2棵子树,则第三层至少有 2 ⌈ m / 2 ⌉ 2\lceil m/2\rceil 2m/2个结点……第 h + 1 h+1 h+1层至少有 2 ( ⌈ m / 2 ⌉ ) h − 1 2(\lceil m/2\rceil)^{h-1} 2(m/2)h1个结点,注意到第 h + 1 h+1 h+1层是不包含任何信息的叶结点。对于关键字个数为的B树,叶结点即查找不成功的结点为 n + 1 n+1 n+1,由此有 n + 1 ≥ 2 ( ⌈ m / 2 ⌉ ) h − 1 n+1≥2(\lceil m/2\rceil)^{h-1} n+12(m/2)h1,即 h ≤ l o g ⌈ m / 2 ⌉ ( ( n + 1 ) / 2 ) + 1 h≤log_{\lceil m/2\rceil}^{((n+1)/2)}+1 hlogm/2((n+1)/2)+1

B树的查找

在B树上进行查找与二叉查找树很相似,只是每个结点都是多个关键字的有序表,在每个结
点上所做的不是两路分支决定,而是根据该结点的子树所做的多路分支决定。
B树的查找包含两个基本操作:
①在B树中找结点;
②在结点内找关键字。
由于B树常存储在磁盘上,因此前一个查找操作是在磁盘上进行的,而后一个查找操作是在内存中进行的,即在找到目标结点后,先将结点信息读入内存,然后在结点内采用顺序查找法或折半查找法。
在B树上查找到某个结点后,先在有序表中进行查找,若找到则查找成功,一否则按照对应的
指针信息到所指的子树中去查找。
查找到叶结点时(对应指针为空指针),则说明树中没有对应的关键字,查找失败。

B树的插入

在B树中找到插入的位置后,并不能简单地将其添加到终端结点中,因为此时可能会导致整棵树不再满足B树定义中的要求。将关键字key插入B树的过程如下:
1)定位。利用前述的B树查找算法,找出插入该关键字的最低层中的某个非叶结点(在B
树中查找key时,会找到表示查找失败的叶结点;这样就确定了最底层非叶结点的插入位置。注意:插入位置一定是最低层中的某个非叶结点)。
2)插入。在B树中,每个非失败结点的关键字个数都在区间 [ ⌈ m / 2 ⌉ − 1 , m − 1 ] [\lceil m/2\rceil-1, m-1] [m/21,m1]内。插入后的结点关键字个数小于m,可以直接插入;插入后检查被插入结点内关键字的个数,当插入后的结点关键字个数大于m-1时,须对结点进行分裂

分裂
取一个新结点,在插入key后的原结点,从中间位置 ( ⌈ m / 2 ⌉ − 1 ) (\lceil m/2\rceil-1) m/21将其中的关
键字分为两部分,左部分包含的关键字放在原结点中,右部分包含的关键字放到新结点中,中间
位置 ( ⌈ m / 2 ⌉ − 1 ) (\lceil m/2\rceil-1) m/21的结点插入原结点的父结点,若此时导致其父结点的关键字个数也超过了上限,则继续进行这种分裂操作,直至这个过程传到根结点为止,进而导致B树高度增1。
结点分裂示意

B树的删除

当被删关键字 k k k不在终端结点(最低层非叶结点)中时,可以用 k k k的前驱(或后继) k ′ k' k来替
k k k,然后在相应的结点中删除 k ′ k' k,关键字 k ′ k' k必定落在某个终端结点中,则转换成了被关键字在终端节点的情形。
删除非终端结点
当被删关键字在终端结点(最低层非叶结点)中时,有下列三种情况:
1)直接删除关键字。若被删除关键字所在结点的关键字个数 ≥ ⌈ m / 2 ⌉ ≥\lceil m/2\rceil m/2,表明删除该关键字后仍满足B树的定义,则直接删去该关键字。
2)兄弟够借。若被删除关键字所在结点删除前的关键字个数 = ⌈ m / 2 ⌉ − 1 =\lceil m/2\rceil-1 =m/21,且与此结点相邻的右(或左)兄弟结点的关键字个数之 ≥ ⌈ m / 2 ⌉ ≥\lceil m/2\rceil m/2,则需要调整该结点、右(或左)兄弟结点及其双亲结点(父子换位法),以达到新的平衡。
删除终端节点关键字 兄弟够借

3)兄弟不够借。若被删除关键字所在结点删除前的关键字个数 = ⌈ m / 2 ⌉ − 1 =\lceil m/2\rceil-1 =m/21,且此时与该结点相邻的左、右兄弟结点的关键字个数均 = ⌈ m / 2 ⌉ − 1 =\lceil m/2\rceil-1 =m/21,则将关键字删除后与左(或右)兄弟结点及双亲结点中的关键字进行合并。
在合并过程中,双亲结点中的关键字个数会减1。若其双亲结点是根结点且关键字个数减少
至0(根结点关键字个数为1时,有2棵子树),则直接将根结点删除,合并后的新结点成为根;
若双亲结点不是根结点,且关键字个数减少到均 = ⌈ m / 2 ⌉ − 2 =\lceil m/2\rceil-2 =m/22,则又要与它自己的兄弟结点进行调整或合并操作,并重复上述步骤,直至符合B树的要求为止。

兄弟不够借

B+树的基本概念

一棵m阶的B+树需满足下列条件;
1)每个分支结点最多有m棵子树(孩子结点)。
2)非叶根结点至少有两棵子树,其他每个分支结点至少有 ⌈ m / 2 ⌉ \lceil m/2\rceil m/2棵子树,
3)结点的子树个数与关键字个数相等。
4)所有叶结点包含全部关键字及指向相应记录的指针,叶结点中将关键字按大小序排列,并且相邻叶结点按大小顺序相互链接起来。
5)所有分支结点(可视为索引的索引)中仅包含它的各个子结点(即下一级的索引块)中
关键字的最大值及指向其子结点的指针。

m阶的B+树与m阶的B树的主要差异如下:
1)在B+树中,具有n个关键字的结点只含有n棵子树,即每个关键字对应一棵子树:而在B树中,有”n关键字的结点含有n+1棵子树。
2)在B+树中,每个结点(非根内部结点)的关键字个数n的范围是 ⌈ m / 2 ⌉ ≤ n ≤ m \lceil m/2\rceil≤n≤m m/2nm(根结点: 1 ≤ n ≤ m 1≤n≤m 1nm,在B树中,每个结点(非根内部结点)的关键字个数的范围是 ⌈ m / 2 ⌉ − 1 ≤ n ≤ m − 1 \lceil m/2\rceil-1≤n≤m-1 m/21nm1(根结点: 1 ≤ n ≤ m − 1 1≤n≤m-1 1nm1).
3)在B+树中,叶结点包含信息,所有非叶结点仅起索引作用,非叶结点中的每个索引项只
含有对应子树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。
4)在B+树中,叶结点包含了全部关键字,即在非叶结点中出现的关键字也会出现在叶结点中;而在B树中,叶结点包含的关键字和其他结点包含的关键字是不重复的。
B+树结构示意图

习题7.3

选择题

  1. B树的叶结点对于查找失败的情况,对有 n n n个关键字的查找集合进行查找,失败可能性有 n + 1 n+1 n+1种,因此具有 n n n个关键字的 m m m阶B树,应该有 n + 1 n+1 n+1个叶结点。
  2. 一棵 m m m阶的B树中含有 n n n个关键字,则树的最大高度为 h ≤ l o g ⌈ m / 2 ⌉ ( ( n + 1 ) / 2 ) + 1 h≤log_{\lceil m/2\rceil}^{((n+1)/2)}+1 hlogm/2((n+1)/2)+1,最小高度为 h ≥ l o g m ( n + 1 ) h≥log_{m}^{(n+1)} hlogm(n+1)

简答题

  1. 利用B树做文件索引时,若假设磁盘页块的大小是4000B(实际应是2的次幂,此处是
    为了计算方便),指示磁盘地址的指针需要5B,现有20000000个记录构成的文件,每个
    记录为200B,其中包括关键字5B
    试问在这个采用B树作索引的文件中,B树的阶数应为多少?假定文件数据部分未按关键字有序排列,则索引部分需要占用多少磁盘页块?

根据B树的概念,一个索引结点应适应操作系统一次读写的物理记录大小,其大小应取不超
过但最接近一个磁盘页块的大小。假设B树为 m m m阶,一个B树结点最多存放 m − 1 m-1 m1个关键字(5B)
和对应的记录地址(5B)、 m m m个子树指针(5B)和1个指示结点中的实际关键字个数的整数(2B),
则有
( 2 × ( m − 1 ) 【 关 键 字 和 对 应 记 录 地 址 】 + m 【 指 针 】 ) × 5 + 2 ≤ 4000 (2×(m-1)【关键字和对应记录地址】+m【指针】)×5+2≤4000 (2×(m1)+m)×5+24000
计算结果为 m ≤ 267 m≤267 m267
一个索引结点最多可以存放 m − 1 = 266 m-1=266 m1=266个索引项,最少可以存放 ⌈ m / 2 ⌉ − 1 = 133 \lceil m/2\rceil-1=133 m/21=133个索引项。
全部有 n = 20000000 n=20000000 n=20000000个记录,每个记录占用空间200B,每个页块可以存放 4000 / 200 = 20 4000/200=20 4000/200=20个记录,则全部记录分布在20000000/20=1000000个页块中,最多需要占用 1000000 / 133 = 7519 1000000/133=7519 1000000133=7519个磁盘页块作为B树索引,最少需要占用 1000000 / 266 = 3760 1000000/266=3760 1000000266=3760个磁盘页块作为B树索引(注意B树与B+树的不同,B树所有对数据记录的索引项分布在各个层次的结点中,B+树所有对数据记录的索引项都在叶结点中)。

7.4 散列表

基本概念

散列函数
一个把查找表中的关键字映射成该关字对应的地址的函数,记为Hash(key)=Addr(这里的地址可以是数组下标、索引或内存地址等)。
散列函数可能会把两个或两个以上的不同关键字映射到同一地址,称这种情况为冲突,这些发生碰撞的不同关键字称为同义词。

散列表:根据关键字而直接进行访问的数据结构。也就是说,散列表建立了关键字和存储地址之间的一种直接映射关系。

理想情况下,对散列表进行查找的时间复杂度为 O ( 1 ) O(1) O(1),即与表中元素的个数无关.下面分别介绍常用的散列函数和处理冲突的方法。

散列函数构造方法

1)定义域必须包含全部需要存储的关键字,值域依赖于散列表的大小或地址范围。
2)地址应该能等概率、均匀地分布在整个地址空间中,从而减少冲突。
3)应尽量简单。

直接定址法

直接取关键字的某个线性函数值为散列地址,散列函数为
H ( k e y ) = k e y H(key)=key H(key)=key H ( k e y ) = a x k e y + b H(key)=axkey+b H(key)=axkey+b
式中,a和b是常数。这种方法计算最简单,且不会产生冲突。它适合关键字的分布基本连续的情况,若关键字分布不连续,窄位较多,则会造成存储空间的浪费。

除留余数法

这是一种最简单、最常用的方法,假定散列表表长为m,取一个不大于m但最接近或等于m的质数p,利用以下公式把关键字转换成散列地址。散列函数为
H ( k e y ) = k e y H(key)=key%p H(key)=key
除留余数法的关键是选好p使得每个关键字通过该函数转换后等概率地映射到散列空间上的任一地址,从而尽可能减少冲突的可能性。

数字分析法

设关键字是r进制数(如十进制数),而r个数在各位上出现的频率不一定相同.可能在某
些位上分布均匀一些,每种数码出现的机会均等;而在某些位上分布不均匀,只有某几种数码经常出现,此时应选取数码分布较为均匀的若干位作为散列地址。这种方法适合于己知的关键字集合,若更换了关键字,则需要重新构造新的散列函数。

平方取中法

顾名思义,这种方法取关键字的平方值的中间几位作为散列地址。具体取多少位要视实际情
况而定.这种方法得到的散列地址与关键字的每位都有关系,因此使得散列地址分布比较均匀,适用于关键字的每位取值都不够均匀或均小于散列地址所需的位数。

处理冲突的方法

开放定址法

可存放新表项的空闲地址既向它的同义词表项开放,又向它的非同义词表项开放。其数学递推公式为
H i = ( H ( k e y ) + d i ) H_i=(H(key)+d_i)%m Hi=(H(key)+di)
式中 H ( k e y ) H(key) H(key)为散列函数; i = 0 , 1 , 2 , . . . , k ( k ≤ m − 1 ) ; i=0,1,2,...,k(k≤m-1); i=0,1,2,...,k(km1)m 表 示 散 列 表 表 长 ; 表示散列表表长; d_i$为增量序列。

取定某一增量序列后,对应的处理方法就是确定的。通常有以下4种取法.
线性探测法
d i = 0 , 1 , 2 , . . . , m − 1 d_i=0,1,2,...,m-1 di=0,1,2,...,m1时,称为线性探测法。这种方法的特点是:冲突发生时,顺序查看表中下一个单元(探测到表尾地址m-1时,下一个探地址是表首地址0),直到找出一个空闲单元(当表未填满时一定能找到一个空闲单元)或查遍个表。
线性探测法可能使第i个散列地址的同义词存入第i+1个散列地址,这样本应存入第i+1个散列地址的元素就争夺第i+2个散列地址的元素的地址“……从而造成大量元素在相邻的散列地址上的聚集或堆积起来,大大降低了查找效率。

平方探测法
d i = 0 2 , 1 2 , ( − 1 ) 2 , 2 2 , ( − 2 ) 2 , . . . , k 2 , ( − k ) 2 d_i=0^2,1^2,(-1)^2,2^2,(-2)^2,...,k^2,(-k)^2 di=02,12,(1)2,22,(2)2,...,k2,(k)2时,称为平方探测法,其中 k ≤ m / 2 k≤m/2 km/2,散列
表长度m必须是一个可以表示成 4 k + 3 4k+3 4k+3的素数,又称二次探测法。
平方探测法是一种较好的处理冲突的方法,可以避免出现“堆积”向题,它的缺点是不
能探到散列表上的所有单元,但至少能探测到一半单元。

再散列法
d i = H a s h 2 ( k e y ) d_i=Hash_2(key) di=Hash2(key)时,称为再散列法,又称双散列法。需要使用两个散列函数,当通过第一个散列函数H(key)得到的地址发生冲突时,则利用第二个散列函数Hash2(key)计算该关键字的地址增量。它的具体散列承数形式如下; H i = ( H ( k e y ) + i × H a s h 2 ( k e y ) ) % m H_i=(H(key)+i×Hash_2(key))%m Hi=(H(key)+i×Hash2(key))m
初始探测位置 H 0 = H ( k e y ) % m H_0=H(key)%m H0=H(key)m,i是冲突的次数,初始为0。在再散列法中,最多经过m一1次探测会遍历表中所有位置,回到 H 0 H_0 H0位置。

伪随机序列法
d i = d_i= di=伪随机数序列时,称为伪随机序列。

注意:在开放定址的情形下,不能随便物理除表中的已有元素,因为若删除元素,则会截断其他具有相同散列地址的元素的查找地址.因此,要删除一个元素时,可给它做一个删除标记,
进行逻辑删除。但这样做的副作用是:执行多次删除后,表面上看起来散列表很满,实际上有许多位置未利用,因此需要定期维护散列表,要把删除标记的元素物理删除。

拉链法

显然,对于不同的关键字可能会通过散列函敖映射到同一地址,为了避免非同义词发生冲
突,可以把所有的同义词存储在“个线性链表中,这个线性链表山其散列地址唯一标识。假设散列地址为i的同义词表的头指针存放在散列表的第个单元中,因而查找、插入和删除操作主要在同义词链中进行。拉链法适用于经常进行插入和删除的情况

散列查找及性能分析

初始化:Addr=Hash(key);
1)检测查找表中地址为Addr的位置上是否有记录,若无记录,返回查找失败;若有记录,比较它与key的值,若相等,则返回查找成功,否则执行(2)。
2)用给定的处理冲突方法计算下一个散列地址,并把Addr置为此地址,转回步骤(1)。

对同一组关键字,设定相同的散列函数,则不同的处理冲突的方法得到的散列表不同,它们
的平均查找长度也不同,本例与上节采用拉链法的平均查找长度不同。
从散列表的查找过程可见:
(1)虽然散列表在关键字与记录的存储位置之间建立了直接映像,但由于“冲突”的产生,使
得散列表的查找过程仍然是一个给定值和关键字进行比较的过程。因此,仍需要以平均查找长度作为衡量散列表的查找效率的度量。

(2)散列表的查找效率取决于三不因素:散列函数、处理冲突的方法和装填因子。
装填因子。散列表的装填因子一般记为α,定义为一个表的装满程度,即 α = 表 中 记 录 数 n 散 列 表 长 度 m \alpha=\frac{表中记录数n}{散列表长度m} α=mn
散列表的平均查找长度依赖于散列表的装填因子而不直接依赖于n或m。直观地看,α
越大,表示装填的记录越“满”,发生冲突的可能性越大,反之发生冲突的可能性越小。
读者应能在给出散列表的长度、元素个数及散列函数和解决冲突的方法后,在求出散列表的基
础上计算出查找成功时的平均查找长度和查找不成功的平均查找长度。

习题7.2

选择题

  1. 含有 n n n个表项的散列表,用线性探测法解决冲突,平均探测次数不超过β,则散列表应能够容纳 m m m项,满足 n m = 1 − 1 2 β − 1 \frac{n}{m}=1-\frac{1}{2\beta-1} mn=12β11(Q6)
  2. K个关键词互为同义词,用线性探测法把K个关键字散列表,至少要进行 K ( K + 1 ) 2 \frac{K(K+1)}{2} 2K(K+1)

简答题

  1. 若要在散列表中删除一个记录,应如何操作?为什么?
    如果是链表则直接物理删除;如果是开放定址法,只能做删除标记。改地址可能是该记录的同义词查找路径上的地址,如果物理地删除就中断了查找路径,因为查找时碰到空地址就认为是失败。

装载因子是用来计算散列表所有地址长度的。

数据结构(C语言第2版) 课后习题答案之 第七章 查找 第7章 查找 1.选择题 (1)对n个元素的表顺序查找,若查找每个元素的概率相同,则平均查找长度为( )。 A.(n-1)/2 B.n/2 C.(n+1)/2D.n (2)适用于折半查找的表的存储方式及元素排列要求为( )。 A.链接方式存储,元素无序 B.链接方式存储,元素有序 C.顺序方式存储,元素无序 D.顺序方式存储,元... 阅读详情

相关推荐

数据结构(C语言第2版) 课后习题答案 详细汇总

数据结构(C语言第2版) 课后习题答案之第一章绪论 数据结构(C语言第2版) 课后习题答案之第二章 线性表 数据结构(C语言第2版) 课后习题答案之 第三章 栈和队列 数据结构(C语言第2版) 课后习题答案之第四章 串、数组和广义表 数据结构(C语言第2版) 课后习题答案之第五章 和二叉 数据结构(C语言第2版) 课后习题答案之 第六章 图 数据结构(C语言第2版) 课后习题答案之 第七章 查找 数据结构(C语言第2版) 课后习题答案之 第八章 排序 ...

Yummy的博客 8644

数据结构查找习题及答案

一、填空 1、顺序查找法等概率下查找成功的平均查找长度为(),查找不成功的比较次数为()。 2、对线性表进行折半查找,要求线性表必须()。 3、设哈希表L长m=14,哈希函数H(key)=key%11,表中已...... ........ 五、设有3阶B-如下,画出删除关键字值17的过程。     六、设哈希函数H(key)=key%13,用公共溢出区法处理冲突。试在0~19的散列地址空间中对关键字序列(19,01,23,14,55,20,84,27,68,11,10,77)造哈希表,并求等概率下查找成功的平均查找长度。 .......

C语言试题178之实现分查找算法,索引顺序查找算法

题目: 例如,采用分查找法在有序表 11、12、18、28、39、56、69、89、96、122、135、146、156、256、298 中查找关键字为 96 的元素。査找特定关键字元素个数为 15,要求用户输入有序表各元素,程序输出査找成功与否,若成功,还显示元素在有序表中的位罝。实现过程: (1)定义结构体 index,用于存储的结构,并定义该结构体数组 index_table。(2)自定义函数 block_search(),实现分查找。(3) main() 函数作为程序的入口函数。程序代码如下:

码莎拉蒂 3168

数据结构---第七章查找---B与B+---应用题

数据结构---第七章查找---B与B+---应用题

programmer9的博客 1227

(王道考研习题-数据结构利用 B 文件索引,若假设磁盘大小是 4 000 字节,指示磁盘地址的指针需要 5 个字节

啊啊啊

qq_43776544的博客 3916

数据结构 | 【查找】考研相关结论与例题

采用顺序查找算法,对有序表从头开始查找,发现当前元素已不小于待查元素,停止查找,确定查找不成功,已知查找任一元素的概率是相同的,则在两种表中成功查找().【解析】对于顺序查找,不管线性表是有序的还是无序的,成功查找第一个元素的比较次数为1,成功查找第二个元素的比较次数为2,以此类推,即。2、已知一个长度为16的顺序表,其元素按关键字有序排列,若采用折半查找查找一个中不存在的元素,则比较的次数至少是(),至多是()。D选项,1、10相加除二向下取整,6、7相加除二向上取整,矛盾。下列二叉中,可能成为折半

"You are worthy! You can do it!" 4735

2005年计算机课程,清华大学2005年计算机考研试

数据结构:第一题:(15分)1、什麽是线性表,线性表的每一个表元素是否必须类型相同?为什么?2、线性表有两种存储形式:顺序表和单链表。线性表(Class LinearList): Class SeqList: Public LinearList(顺序表)(抽象基类)(派生类)Class LinkedList: Public Linearlist(单链表)如果需要定义和使用一个线性表对象,试问在程序...

weixin_39557199的博客 544

数据结构》-第七章 查找(习题)

第七章 查找 对前几章这些数据结构的产生相应运算—查找。关于查找的不同算法为每年考试考查的重点,因此需要重点把握各个结构包括的查找方法及查找删除等操作的过程。对散列结构主要,应学握散列表的构造、冲突处理方法(各种方法的处理过程)、查找成功和查找失败的平均查找长度、散列查找的特征和性能分析。 因此,在考试过程中,会涉及多种题型,同所占分值较大,要对本章内容重点把握。 一、选择题 1.如果要求一个线性表既能较快的查找,又能适应动态变化的要求,最好采用 ( )查找法。 ...

大家一起进步叭 1万+

25版王道数据结构课后习题详细分析 第七章查找 7.3查找

一棵二叉排序按先序遍历得到的序列为(50,38,30,45,40,48,70,60,75,80),试画出该 二叉排序,并求出等概率下查找成功和查找失败的平均查找长度。 按照序列(40,72,38,35,67,51,90,8,55,21)建立一棵二叉排序,画出该,并求出在等概率的情况下,查找成功的平均查找长度依次把结点(34,23,15,98,115,28,107)插入初始状态为空的平衡二叉排序,使得在每次插入后保持该仍然是平衡二叉。请依次画出每次插入后所形成的平衡二叉排序

2406_86301373的博客 1392

25版王道数据结构课后习题详细分析 第七章查找 7.1、7.2查找的基本概念,顺序查找和折半查找

顺序查找适合于存储结构为的线性表对有序表从头开始查找,发现当前元素已不小于待查元素,停止查找,确定查找不成功,已 知查找任意一个元素的概率是相同的,则在两种表中成功查找对长度为n的有序单链表,若查找每个元素的概率相等,则顺序查找表中任意一个元素的查找成功的平均查找长度为对长度为3的顺序表进行查找,若查找第一个元素的概率为 1/2,查找第二个元素的概 率为1/3,查找第三个元素的概率为1/6,则查找任意一个元素的平均查找长度为下列关于二分查找的叙述中,正确的是

2406_86301373的博客 1661

数据结构习题解析与实验指导第七章(查找)课后算法设计题

1.试写出折半查找的递归算法。 #include <iostream> using namespace std; int find(int data[], int start, int end, int k) { if (start < end) { int mid = (start + end) / 2; if (data[mid] == k) { return mid; } else if (k < data[mid]) { return fin

学者的博客 1427

25版王道数据结构课后习题详细分析 第七章查找 7.5散列表

只能在顺序存储结构上进行的查找方法是( )。 散列查找一般适用于( )的情况下的查找。 下列关于散列表的说法中,正确的是( )。 在开放定址法中散列到同一个地址而引起的“堆积”问题是由于( )引起的。 下列关于散列冲突处理方法的说法中,正确的有( )。 假定有 K个关键字互为同义词,若用线性探测法把这 K个关键字填入散列表,至少要进行( )次探测。 对包含n个元素的散列表进行查找,平均查找长度( )。采用开放定址法解决冲突的散列查找中,发生聚集的原因主要是

2406_86301373的博客 1353

(期末考试prepare)数据结构(C语言版)第七章——查找(顺序表、表、哈希表)·附习题

数据结构课程候写的期末复习笔记,根据考点走,这篇博文是查找(顺序表、表、哈希表)等

111辄的博客 1889

第七章 查找——课后习题解练【数据结构(c语言版 第2版)】

答案:CDC。

naozibuok的博客 515

数据结构与算法--PTA第七章习题

广东外语外贸大学-数据结构与算法--PTA第七章习题答案; 将M个元素存入用长度为S的数组表示的散列表,则该表的装填因子为M/S 已知一个长度为16的顺序表L,其元素按关键字有序排列。若采用二分查找查找一个L中不存在的元素,则关键字的比较次数最多是

hcjdhsjs的博客 1172

25版王道数据结构课后习题详细分析 第七章查找 7.4B和B+

下图所示是一棵下列关于m阶B的说法中,错误的是以下关于m阶B的说法中,正确的是在一棵m阶 B插入操作前,若一个结点中的关键字个数等于则插入操作后必须分裂成两个结点;在一棵m阶 B删除操作前,若一个结点中的关键字个数等于则删除操作后可能需要同它的左兄弟或右兄弟结点合并成一个结点。 具有n个关键字的m阶B,应有个叶结点。 高度为5的3阶B至少有个结点,至多有个结点。

2406_86301373的博客 1781

数据结构》王道 第七章 查找

数据结构》王道 第七章 查找 1. 查找的基本概念1.1 基本概念1.2 算法的评价指标2. 顺序查找2.1 算法思想2.2 算法实现2.2.1 常规顺序查找2.2.2 带哨兵的顺序查找2.3 效率分析2.4 优化2.4.1 对有序表2.4.2 被查概率不相等 1. 查找的基本概念 1.1 基本概念 1.2 算法的评价指标 查找成功的情况下: 查找失败的情况下: 2. 顺序查找 2.1 算法思想 2.2 算法实现 2.2.1 常规顺序查找 2.2.2 带哨兵的顺序查找 优点:无需判断

壹的博客 2990

数据结构 第七章查找

linguel的博客 266

数据结构》-第七章 查找(知识点总结)

第七章 查找 本章开始介绍关于前几章这些数据结构的相应的运算—查找。关于查找的不同算法为每年考试考查的重点,主要分为线性结构的查找形结构的查找、散列结构的查找及字符串模式匹配,同分析各个查找方法的查找效率。其中线性结构的查找(静态查找表)主要代表为顺序查找、分查找和折半查找形结构的查找(动态查找表)为二叉排序、二叉平衡、B和B+;B和B+作为本章的难点。对于B,要求掌握插入、删除和查找的操作过程:对于B+,仅要求了解其基本概念和性质。 散列结构主要以散列表(哈希表)为代表,应

大家一起进步叭 8639
上一篇: 数据结构 第六章 图 习题
下一篇: 数据结构 第八章 排序 习题
AnthonyM08
博客等级 码龄8年 26粉丝 37原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值