数据结构习题解析.docx
《数据结构习题解析.docx》由会员分享,可在线阅读,更多相关《数据结构习题解析.docx(28页珍藏版)》请在冰豆网上搜索。
![数据结构习题解析.docx](https://file1.bdocx.com/fileroot1/2022-11/26/f6592368-5c42-415d-9786-785991cbbd58/f6592368-5c42-415d-9786-785991cbbd581.gif)
数据结构习题解析
第10章索引与散列
一、复习要点
索引结构和散列结构是用于外部搜索的搜索结构。
数据在外存的组织即文件结构,主要分顺序.直接存取(散列)和索引文件。
在这些文件组织中使用的主要是索引和散列方法a
1、基本知识点
要求掌握静态索引结构,包括线性索引、倒排索引.静态索引树的搜索和构造方法。
掌握动态索引结构,包括B树的搜索、插入、删除,通过关键码个数估算B树的髙度的方法;B+树的搜索、插入与删除。
掌握散列法,包括散列函数的构造、处理溢出的闭散列方法:
处理溢岀的开散列方法:
散列表分析。
二、难点与重点
1、线性索引
密集索引、稀疏索引、索引表计算
基于属性查找建立倒排索引.单元式倒排表
2、动态搜索树
平衡的m路搜索树的泄义.搜索算法
B树的定义、B树与平衡的m路搜索树的关系
>B树的插入(包括结点分裂)、删除(包括结点调整与合并)方法
B树中结点个数与髙度的关系
B+树的泄义、搜索、插入与删除的方法
3、散列表
>散列函数的比较
>装载因子与平均搜索长度的关系,平均搜索长度的关系
>表长m、表中已有数据对象个数n和装载因子的关系
>解决冲突的(闭散列)线性探查法的运用,平均探查次数的计算
>线性探查法的删除问题、散列表类的设计中必须为各地址设置三个状态
>线性探查法中的堆积聚集问题
>解决冲突的(闭散列)双散列法的运用,平均探查次数计算
>双散列法中再散列函数的设计要求与表长m互质,为此m设计为质数较宜
>解决冲突的(闭散列)二次散列法的运用,平均探查次数计算
>注意:
二次散列法中装载因子与表长m的设置
>解决冲突的(开散列)开散列法的运用,平均探查次数汁算
>由平均探查次数计算装载因子,再计算表大小的方法
三、教材中习题的解析
10-1什么是静态索引结构?
什么是动态索引结构?
它们各有哪些优缺点?
【解答】
静态索引结构指这种索引结构在初始创建,数据装入时就已经左型,而且在整个系统运行期间,树的结构不发生变化,只是数据在更新。
动态索引结构是指在整个系统运行期间,树的结构随数拯的增删及时调整,以保持最佳的搜索效率。
静态索引结构的优点是结构泄型,建立方法简单,存取方便;缺点是不利于更新,插入或删除时效率低。
动态索引结构的优点是在插入或删除时能够自动调整索引树结构,以保持最佳的搜索效率:
缺点是实现算法复杂。
10-2设有10000个记录对象,通过分块划分为若干子表并建立索引,那么为了提高搜索效率,每一个子表的大小应设计为多大?
【解答】
每个子表的大小s=n=10000二100个记录对象。
10-3如果一个磁盘页块大小为1024(二1K)字节,存储的每个记录对象需要占用16字节,其中关键码占4字节,其它数据占12字节。
所有记录均已按关键码有序地存储在磁盘文件中,每个页块的第1个记录用于存放线性索引。
另外在内存中开辟了256K字节的空间可用于存放线性索引。
试问:
(1)若将线性索引常驻内存,文件中最多可以存放多少个记录?
(每个索引项8字节,其中关键码4字节,地址4字节)
(2)如果使用二级索引,第二级索引占用1024字节(有128个索引项),这时文件中最多可以存放多少个记录?
【解答】
(1)因为一个磁盘页块大小为1024字节,每个记录对彖需要占用16字节,则每个页块可存放1024/16=64个记录,除第一个记录存储线性索引外,每个页块可存储63个记录对象。
又因为在磁盘文件中所有记录对象按关键码有序存储,所以线性索引可以是稀疏索引,每一个索引项存放一个页块的最大关键码及该页块的地址。
若线性索引常驻内存,那么它最多可存放256*(1024/8)=256*128=32768个索引项,文件中可存放32768*63=2064384个记录对象。
(2)由于第二级索引占用1024个字节,内存中还剩255K字节用于第一级索引。
第一级索引有255*128=32640个索引项,作为稀疏索引,每个索引项索引一个页块,则索引文件中可存放32640*63二2056320^
397
HelloWorld!
82
XYZ
1038
Thisstringisratherlong
1037
ThisisShorter
42
ABC
2222
HellonewWorld!
10-4假设在数据库文件中的每一个记录是由占2个字节的整型数关键码和一个变长的数据字段组成。
数据字段都是字符串。
为了存放右而的那些记录,应如何组织线性索引?
【解答】
将所有字符串依加入的先后次序存放于一个连续的存储空间store中,这个空间也叫做“堆”,它是存放所有字符串的顺序文件。
它有一个指针free,指示在堆store中当前可存放数据的开始地址。
初始时free置为0,表示可从文件的0号位置开始存放。
线性索引中每个索引项给岀记录关键码,字符串在store中的起始地址和字符串的长度:
关键码
串长度
串起始地址
42
3
56
82
3
12
397
12
0
1037
15
41
1038
26
15
2222
16
59
HelloWorld!
XY2ThisstringisratherlongThistff
isShorterABCHellonewWorld!
tf仲
10-5设有一个职工文件:
记录地址职工号姓名性别职业年龄籍贯丿J工资(元)
10032
034
刘激扬
男
教师
29
山东
720.00
10068
064
蔡晓莉
•k
教师
32
辽宁
1200.00
10104
073
朱力
男
实验员
26
广东
480.00
10140
081
洪伟
男
教师
36
北京
1400.00
10176
092
卢声凯
男
教师
28
湖北
720.00
10212
123
林徳康
男
行政秘书
33
江西
480.00
10248
110
熊南燕
•z
教师
27
上海
780.00
10284
175
吕颖
女
实验员
28
江苏
480.00
10320209袁秋艺女教师24广东720.00
其中,关键码为职工号。
试根据此文件,对下列查询组织主索引和倒排索引,再写出搜索结果关键码。
(1)男性职工:
(2)月工资超过800元的职工:
(3)月工资超过平均工资的职工;(4)职业为实验员和行政秘书的男性职工:
(5)男性教师或者年龄超过25岁且职业为实验员和教师的女性职工。
【解答】
主索引
月工资倒排索引
职务倒排索引
职工号
记录地址
034
10032
061
10068
073
10101
081
10110
092
10176
123
10212
140
10248
175
10284
209
10320
丿J工资
长度
指针
480.
3
073
123
175
720.
3
031
092
209
780.
1
140
1200.
1
064
1400.
1
081
职务
长度
指针
教师
6
034
064
081
092
140
209
实验员
2
073
175
行政秘书
1
123
性别
长度
指针
男
5
034
073
081
092
123
女
4
064
140
175
209
搜索结果:
年龄
长度
指针
24
1
209
26
1
073
27
1
140
28
2
092
175
29
1
031
32
1
064
33
1
123
36
1
081
(1)男性职工(搜索性别倒排索引):
(034,073,081,092,123}
(2)月工资超过800元的职工(搜索月工资倒排索引):
{064,081}
(3)月工资超过平均工资的职工(搜索月工资倒排索引){月平均工资776元}:
(064,081,140}
⑷职业为实验员和行政秘书的男性职工(搜索职务和性別倒排索引):
{073,123,175}&&{034,073,081,092,123}={073,123}
(5)男性教师(搜索性别与职务倒排索引):
{034,073,081,092,123}&&{034,064,081,092,140,209}={034,081,092}
年龄超过25岁且职业为实验员和教师的女性职工(搜索性别、职务和年龄倒排索引):
{064,140,175,209}&&{034,064,073,081,092,140,175,209}&&{034,061,073,081,092,123,140,175}={064,140,175}
10-6倒排索引中的记录地址可以是记录的实际存放地址,也可以是记录的关键码。
试比较这两种方式的优缺点。
【解答】
在倒排索引中的记录地址用记录的实际存放地址,搜索的速度快:
但以后在文件中插入或删除记录对象时需要移动文件中的记录对象,从而改变记录的实际存放地址,这将对所有的索引产生影响:
修改所有倒排索引的指针,不但工作量大而且容易引入新的错误或遗漏,使得系统不易维护。
记录地址采用记录的关键码,缺点是寻找实际记录对象需要再经过主索引,降低了搜索速度;但以后在文件中插入或删除记录对象时,如果移动文件中的记录对象,导致许多记录对象的实际存放地址发生变化,只需改变主索引中的相应记录地址,英他倒排索引中的指针一律不变,使得系统容易维护,且不易产生新的错误和遗漏。
10-7m=2的平衡m路搜索树是AVL树,m=3的平衡m路搜索树是2-3树。
它们的叶结点必须在同一层吗?
m阶B树是平衡m路搜索树,反过来,平衡m路搜索树一左是B树吗?
为什么?
【解答】
m二3的平衡m路搜索树的叶结点不一定在同一层,而m阶B_树的叶结点必须在同一层,所以m阶B_树是平衡m路搜索树,反过来,平衡m路搜索树不一立是B_树。
10-8下图是一个3阶B树。
试分别画出在插入65、15、40、30之后B树的变化。
【解答】
插入65后:
10-9下图是一个3阶B树。
试分别画出在删除50.40之后B树的变化。
【解答】
删除50后:
删除40后:
10-10对于一棵有1999999个关键码的199阶B树,试估讣其最大层数(不包括失败结点)及最小层数(不包括失败结点)o
【解答】
设B树的阶数m二199,则m/2二100。
若不包括失败结点层,则其最大层数为
logx-((N+l)/2)=logioo1000000=3
若使得每一层关键码数达到最大,可使其层数达到最小。
第0层最多有(m-l)个关键码,第1层最多有m(m-l)个关键码,第2层最多有m:
(m-1)个关键码,,第h-l层最多有nT‘(m-1)个关键码。
层数为h的B树最多有(m-1)+m(m~l)+m:
(m-1)+…+(m-1)=(m-1)(mh-1)/(m-1)二nf-1个关键码。
反之,若有n个关键码,nWn?
-l,则h>loga(n+1),所以,有1999999个关键码的199阶B树的最小层数为
logo(n+1)=log!
99(1999999+1)=log2000000=310-11给定一组记录,其关键码为字符。
记录的插入顺序为{C,S,D,T,A,M,P,I,B,W,N,G,U,R,K,E,H,0,L,J}.给岀插入这些记录后的4阶B+树。
假立叶结点最多可存放3个记录。
【解答】
插入R
10-12设有一棵B+树,其内部结点最多可存放100个子女,叶结点最多可存储15个记录。
对于1,2,3,4,5层的B+树,最多能存储多少记录,最少能存储多少记录。
【解答】
一层阶树:
根据B+树左义,一层B+树的结点只有一个,它既是根结点又是叶结点,最多可存储ml二15个记录,最少可存储ml/2二8个记录。
二层B+树:
第0层是根结点,它最多有m=100棵子树,最少有2个结点:
第1层是叶结点,它最多有m个结点,最多可存储m*ml=100*15=1500个记录,最少有2个结点,最少可存储2*ml/2二16个记录。
三层B+树:
第2层是叶结点。
它最多有恋个结点,最多可存储m2*ml二150000个记录。
最少有2*m/2=100个结点,最少可存储2*m/2*ml/2=800个记录。
四层B+树:
第3层是叶结点。
它最多有个结点,可存储m'*ml=15000000个记录。
最少有茁m/22=2*50’二5000个结点,存储2*m/2:
*ml/2=40000个
记录。
五层B+树:
第4层是叶结点。
它最多有个结点,可存储m1*ml=1500000000个记录。
最少有2*m/2、二2*50'二250000个结点,存储2*m/2'*ml/2=2000000
个记录。
10-13设散列表为HT[13],散列函数为H(Aey)二辰用闭散列法解决冲突,对下列关键码序列12,23,45,57,20,03,78,31,15,36造表。
(1)采用线性探查法寻找下一个空位,画出相应的散列表,并计算等概率下搜索成功的平均搜索长度和搜索不成功的平均搜索长度。
(2)采用双散列法寻找下一个空位,再散列函数为RH(key)=(7*^y)%10+1,寻找下一个空位的公式为(H-+RH(W)%13,乩二HS°画出相应的散列表,并计算等概率下搜索成功的平均搜索长度。
【解答】
使用散列函数H(Aey)二keymod13,有
H(12)=12,H(23)二10,H(45)=6,H(57)二5,
H(20)=7fH(03)=3,H(78)=0,H(31)二5,
H(15)=2、H(36)=10.
(1)利用线性探查法造表:
0
1
2
3
4
5
6
7
8
9
10
11
12
78
15
03
57
45
20
31
23
36
12
(1)
(1)
(1)
(1)
(1)
(1)
(4)
(1)
(2)
(1)
搜索成功的平均搜索长度为
ASS=-^7(1+1+1+1+1+1+44-1+2+1)=^
搜索不成功的平均搜索长度为
136
ASLunsuctf=TT(2+l+3+2+l+5+4+3+2+l+5+4+3)=
(2)利用双散列法适表:
Hi=(Hx-i+RH(key))%13,乩二H(key)
01
2
34
5
6
7
8
9
1011
12
78
15
03
57
45
20
31
36
23
12
(1)
(1)
(1)
(1)
(1)
(1)
(3)
(5)
(1)
(1)
搜索成功的平均搜索长度为
1z、
16
AS—=—(l+l+l+l+l+l+3+5+l+l)=
=75
10-14设有150个记录要存储到散列表中,要求利用线性探査法解决冲突,同时要求找到所需记录的平均比较次数不超过2次。
试问散列表需要设计多大?
设是散列表的装载因子,则有
【解答】
已知要存储的记录数为n=150,查找成功的平均査找长度为ASL*2,则有ASL沃
1、…2亠n1502
2,解得亍。
又有=则m225。
10-15若设散列表的大小为m,利用散列函数计算岀的散列地址为h二hash(x)o
(1)试证明:
如果二次探査的顺序为(h+q3),(h+(q-l)=),…,(h+1),h,(h-1),…,(h-q2),其中,q二(m-l)/2o因此在相继被探査的两个桶之间地址相减所得的差取模(細)的结果为
m-2,m-4,m-6,5,3,1,1,3,5,•••,m-6,m-4,m-2
(2)编写一个算法,使用课文中讨论的散列函数h(x)和二次探查解决冲突的方法,按给世值X来搜索一个大小为m的散列表。
如果x不在表中,则将它插入到表中。
【解答】
(1)将探査序列分两部分讨论:
(h+q2),(h+(q-l):
),…,(h+1),h和(h~l),(h-22),•••,(h-q:
)o
对于前一部分,设其通项为h+(q-d)\d=0,1,q,则相邻两个桶之间地址相减所得的差取模:
(h+(q-(d-1)-(h+(q-d)"))%m=((q-(d-1)-(q-
d):
)%m
=(2*q-2*d+1)%m=(m-2*d)%m・(代换q二(旷1)/2)
代入d二1,2,…,q,则可得到探査序列如下:
m-2,m~4,m-6,•••,5,3,1°(m-2*q=m-2*(m-l)/2=1)
对于后一部分,其通项为h-(q-d)\d=q,q+1,…,2q,则相邻两个桶之间地址相减所得的差取模:
(h-(q-d)*-(h-(q-(d+1))"))%m=((q-(d+1)"-(q一d)2)%m
=(2*d-2*q+1)%m=(2*d-m+2)%m(代换q二(m-l)/2)代入d二q,q+1,…,2q-l,则可得到
2*d-m+2=2*q-m+2=m-l-m+2=l,
2*d-m+2=2*q+2-m+2=m-1+2-m+2=3,,
2*d-m+2二2*(2*q~l)-m+2=2*(m-1-1)-m+2=2*m-4-m+2=ni-2。
in正毕』
(2)编写算法
下而是使用二次探查法处理溢出时的散列表类的声明。
templateclassHashTable{I枚列表类的定义
public:
enumKindOfEntry{Active,Empty,Deleted};〃表项分类(活动/空/删)
HashTable():
TableSize(DefaultSize){AllocateHt();CurrentSize=0;}//
构造函数
"HashTable(){delete1Jht;}〃析构函数
constHashTable&operator=(constHashTable&ht2);//重载函数:
表賦值
7intFind(constType&x);,/在散列表中搜索x
intIsEmpty(){return!
CurrentSize?
1:
0;}//判散列表空否.空则返回1
private:
structHashEntry{//散列表的表项定义
TypeElement;//表项的数据,即表项的关键码
KindOfEntryinfo;//三种状态:
Active,Empty,Deleted
HashEntry():
info(Empty){}//表项构造函数
HashEntry(constType&E,KindOfEntryi=Empty):
Element(E),mfo(i){}
enum{DefualtSize=31;}
HashEntry*ht;//散列表存储数组
intTableSize;//数组长度.是满足4k+3的质数.k是整数
intCurrentSize;//已占据散列地址数目
voidAllocateHt(){ht=newHashEntry[TableSize];}//为散列表分配存储空间;
intFindPos(constType&x);/散列函数
};
template〈classType>constHashTable&HashTable:
:
operator=(constHashTabletht2){
//重载函数:
复制一个散列表ht2
if(this!
=4ht2){
delete11ht;TableSize=ht2.TableSize;AllocateHt();//重新分配目标散
列表存储空间
for(inti=0;i=ht2.ht[i];//从源散列表
向目标散列表传送
CurrentSize=ht2.CurrentSize;//传送'"l前表项个数
}
return枕his;//返回目标散列表结构抬针
}
templateintHashTable:
:
Find(constTypeftx){
//共有函数:
找下一散列位置的函数
inti=0,q=(TableSize-1)/2,hO;//i为探査次数
intCurrentPos=hO=HashPos(x);//利用散列函数计算x的散列地址
while(ht[CurrentPos].info!
=Empty&&ht[CurrentPos].Element!
=x){
/搜索是否要求表项
if(i<=q){〃求“下一个”桶
CurrentPos=hO+(q"i)*(q"i);
vhile(CurrentPos>=TableSize)CurrentPos■=TableSize;//实现取模
else(
CurrentPos=hO-(i-q)*(i-q);
while(CurrentPos<0)CurrentPos+=TableSize;//实现取模
}
i++;
}
if(ht[CurrentPos].info==Active&&ht[CurrentPo