数据结构答案 78 章.docx
《数据结构答案 78 章.docx》由会员分享,可在线阅读,更多相关《数据结构答案 78 章.docx(19页珍藏版)》请在冰豆网上搜索。
数据结构答案78章
第7章排序
7.1回答下列概念:
稳定排序、内排序
排序码相同的两个记录经过排序之后,其相对次序保持不变,称该排序方法是稳定的;反之,称该排序方法是不稳定的。
待排序的文件一般都保存在外存中,当文件比较小时,可将全部记录一次读入内存,使得整个排序过程全部在内存中进行,这种排序称为内部排序。
7.2常用的实现排序的方法有几大类?
它们的实现思想是什么?
插入排序(InsertionSort)的基本思想是:
将一个待排序记录按照排序码的大小插入到一个有序序列的适当位置,使得插入后的序列仍然有序,直到所有记录全部插入到有序序列中。
插入排序主要包括两种方法:
直接插入排序和希尔(Shell)排序。
交换排序的基本思想是:
两两比较待排序记录的排序码,不符合排列顺序则交换记录,直到所有记录的排序码都符合排序要求。
本节主要介绍两种交换排序:
起泡排序和快速排序。
选择排序(SelectionSort)的基本思想是:
每一次从待排序记录序列中选取一个排序码最小(或最大)的记录,放在待排序记录序列的最前面(或最后面),重复此过程,直到所有的记录按排序码排好序。
本节将介绍直接选择排序和堆排序两种方法。
归并排序(MergeSort)是利用“归并”技术实现的排序方法。
所谓归并就是将两个或多个有序表合并成一个有序表的过程。
如果是将两个有序表合并成一个有序表称为二路归并;同理,将三个有序表合并成一个有序表称为三路归并,以此类推可以有n路归并等。
但二路归并是最简单和最常用的。
本节主要讲二路归并技术实现的归并排序。
基数排序(RadixSort)是与前面各类排序方法完全不同的一种排序方法,它是基于排序码的结构分解,然后通过“分配”和“收集”方法实现的排序。
7.3已知一组记录的排序码为(25,45,9,35,80,85,23,40,33,75),分别写出直接插入排序,希尔排序(增量为5,3,1),起泡排序,快速排序,直接选择排序,堆排序,二路归并排序和基数排序的各趟排序结果。
并归纳以上各种排序方法的稳定性,对不稳定的排序方法请举出一个反例。
1.直接插入排序过程:
[25]45935808523403375(初始)
[2545]935808523403375(1趟插入结果)
[92545]35808523403375(2趟插入结果)
[9253545]808523403375(3趟插入结果)
[925354580]8523403375(4趟插入结果)
[92535458085]23403375(5趟插入结果)
[9232535458085]403375(6趟插入结果)
[923253540458085]3375(7趟插入结果)
[92325333540458085]75(8趟插入结果)
[9232533354045758085](9趟插入结果)
稳定的排序。
2.希尔排序过程:
2545935808523403375(初始)
2523933758545403580(1趟希尔排序结果)
2523933403545758580(2趟希尔排序结果)
9232533354045758085(3趟希尔排序结果)
不稳定排序,如34122512,若初始步长为3。
3.起泡排序过程:
2545935808523403375(初始)
25935458023403375[85](1趟起泡排序结果)
925354523403375[8085](2趟起泡排序结果)
9253523403345[758085](3趟起泡排序结果)
92523353340[45758085](4趟起泡排序结果)
9252333354045758085(5趟起泡排序结果,已经有序)
稳定排序。
4.快速排序过程:
2545935808523403375(初始)
{239}25{35808545403375}(1层划分结果)
923253335{8545408075}(2层划分结果)
923253335{75454080}85(3层划分结果)
923253335{4045}758085(4层划分结果)
9232533354045758085(5层划分结果)
不稳定排序,如25,12,12
5.直接选择排序过程:
2545935808523403375(初始)
9[452535808523403375](1趟直接选择排序结果)
923[2535808545403375](2趟直接选择排序结果)
92325[35808545403375](3趟直接选择排序结果)
9232533[808545403575](4趟直接选择排序结果)
923253335[8545408075](5趟直接选择排序结果)
92325333540[45858075](6趟直接选择排序结果)
9232533354045[858075](7趟直接选择排序结果)
923253335404575[8085](8趟直接选择排序结果)
92325333540457580[85](9趟直接选择排序结果)
不稳定排序,如25,25,9
6.堆排序过程:
2545935808523403375(初始)
8580254075923353345(建堆)
80752540459233533[85](1趟堆排序结果)
754525403392335[8085](2趟堆排序结果)
4540253533923[758085](3趟堆排序结果)
40352523339[45758085](4趟堆排序结果)
353325239[4045758085](5趟堆排序结果)
3322259[354045758085](6趟堆排序结果)
25229[33354045758085](7趟堆排序结果)
229[2533354045758085](8趟堆排序结果)
9[222533354045758085](9趟堆排序结果)
不稳定排序,如25,25,9
7.二路归并排序过程:
2545935808523403375(初始)
[2545][935][8085][2340][3375](1趟归并排序结果)
[9253545][23408085][3375](2趟归并排序结果)
[923253540458085][3375](3趟归并排序结果)
[9232533354045758085](4趟归并排序结果)
是稳定的排序。
8.基数排序过程:
2545935808523403375(初始)
8040233325453585759(一趟分配收集结果)
9232533354045758085(二趟分配收集结果)
是稳定的排序。
7.4在堆排序、快速排序和归并排序中,
(1)若从存储空间考虑,应该首先选取哪个方法?
其次选取哪个方法?
最后选取哪个方法?
(2)若从排序结果的稳定性考虑,则应该选取哪个方法?
(3)若只从平均情况下排序最快考虑,应该选取哪个方法?
(4)若只从最坏情况下排序最快并且要节省内存考虑,则应该选取哪个方法?
(1)若从存储空间考虑,应该首先选取堆排序方法,其次选取快速排序方法,最后选取归并排序方法。
(2)若从排序结果的稳定性考虑,则应该选取归并排序。
(3)若只从平均情况下排序最快考虑,应该选取快速排序。
(4)若只从最坏情况下排序最快并且要节省内存考虑,则应该选取堆排序方法。
7.5已知一组记录的排序码为(54,38,96,23,15,72,60,45,83),利用快速排序方法对其进行排序。
(1)递归调用使用的栈所能达到的最大深度是多少?
共需递归调用的次数是多少?
(2)其中,第二次递归调用时是对哪一组记录进行快速排序?
(1)递归调用使用的栈所能达到的最大深度是4,共需递归调用的次数是4次。
(2)其中,第二次递归调用时是对{72,60,96,83}进行快速排序。
7.6设计单链表作存储结构实现直接插入排序和直接选择排序的算法,并分析你设计的算法的时间复杂度和空间复杂度。
typedefstructnode
{intdata;
structnode*next;
}linklist;/*定义单链表*/
直接插入排序算法:
voidinsertSort(linklist*head)
{linklist*p,*q,*s,*t;
p=head->next;q=p->next;p->next=NULL;
while(q){
s=head;p=head->next;
while(p&&p->datadata)
{s=p;p=p->next;
}
s->next=q;t=q->next;q->next=p;
q=t;
}
}/*算法结束*/
时间复杂度:
O(n2),空间复杂度:
O
(1)
直接选择排序算法:
voidsimpleselect(linklist*head)
/*head是单链表的头指针,本算法对其进行直接选择排序。
设p指向无序区第一个记录(开始是链表的第一个记录),用q指向一趟排序中的最小记录,为简便起见,将p和q所指结点的数据交换*/
{linklist*p,*q,*s;
intx;
p=head->next;
while(p->next!
=NULL)//剩最后一个记录时,排序结束
{q=p;//设当前结点为最小
s=p->next;//s指向待比较结点
while(s!
=NULL)
if(s->data>q->data)s=s->next;
else{q=s;s=s->next;}//用指向新的最小
if(q!
=p){x=q->data;q->data=p->data;p->data=x;}
p=p->next;//链表指针后移,指向下一个最小值结点
}
}//算法结束
时间复杂度:
O(n2),空间复杂度:
O
(1)
7.7本书中介绍的起泡排序算法是从表的一端开始两两相邻元素比较,即单向扫描。
设计一个从表的两端交替进行双向扫描的起泡排序算法。
typedefstruct
{intkey;/*定义排序码*/
DataTypeother;/*定义其他数据项*/
}RecType;/*记录的类型*/
RecTypeR[N+1];
voidTwoWayBubbleSort(RecTyper[])
/*对r[1..n]进行双向冒泡排序。
即相邻两遍向两个相反方向起泡*/
{inti=1,exchange=1;//设标记
intj;
RecTypet;
while(exchange)
{exchange=0;//假定本趟无交换
for(j=N-i+1j>=i+1;j--)//向前起泡,一趟有一最小冒出
if(r[j].keyfor(j=i+1;j>=N+i-1;j++)//向后起泡,一趟有一最大沉底
if(r[j].key>r[j+1].key){t=r[j];r[j]=r[j+1];r[j+1]=t;exchange=1;}//有交换
i++;
}}//算法结束
7.8判断下列序列是否为堆?
如果不是,则把它们调整成堆。
(1)(503,87,512,61,908,170,896,275,653,462)
(2)(12,70,33,65,24,48,92,86,33,55)
(3)(100,55,97,30,23,86,60,8,12)
(4)(5,56,18,40,38,27,58,30,78,28,98)
解答:
(1)不是堆,调整为大根堆后为(908,653,896,503,462,170,512,275,61,87)
(2)不是堆,调整为小根堆后(12,24,33,33,55,48,92,86,65,70)
(3)是大根堆
(4)不是堆,调整为小根堆后(5,28,18,30,38,27,58,40,78,56,98)
第8章查找
8.1回答下列概念:
二叉排序树、AVL树、装填因子、冲突、同义词。
二叉排序树(BinarySortTree)又称二叉查找树(BinarySearchTree),它或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于根结点的值;
(3)左、右子树也都是二叉排序树。
平衡二叉排序树(BalancedBinaryTree)简称平衡树,又称AVL树。
平衡树或者是一棵空树,或者是具有下列性质的二叉排序树:
它的左子树和右子树都是平衡二叉树,且左子树和右子树高度之差的绝对值不超过1。
设线性表的长度为n,散列表(一维数组)的长度为m,则称α=n/m为散列表的装填因子。
一般地,若某个散列函数Hash(k)对于不相等的关键字key1和key2,得到相同的散列地址,则称该现象为冲突。
发生冲突的两个不同的关键字key1和key2被称为同义词
8.2已知一个有序表长度为20,求出在等概率情况下,二分查找查找成功和查找不成功的平均查找长度。
查找成功情况下的平均查找长度为:
ASL=(1+2×2+3×4+4×8+5×5)/20=74/20。
查找不成功时的平均查找长度ASL为:
ASL不成功=(4×11+5×10)/21=94/21。
8.3对有序表(12,30,36,50,54,62,67,75,83,92),请画出二分查找的判定树。
查找83(成功)的比较次数是多少?
查找13(失败)的比较次数是多少?
查找83的比较次数为3次,查找13的比较次数
为4次。
8.4表8.7中是一个查找表中各记录的关键字及其查找概率,为了提高顺序查找效率,可以按照概率越高,比较次数越少的原则重新排列记录,请求出按表8.7中的记录顺序和重新排列后的记录顺序进行顺序查找的平均查找长度。
表8.7关键字及其查找概率
34
45
65
12
33
56
64
66
78
90
35
82
0.1
0.12
0.09
0.04
0.23
0.01
0.04
0.13
0.03
0.08
0.07
0.06
(1)按照原顺序进行查找
ASL=1*0.1+2*0.12+3*0.09+4*0.04+5*0.23+6*0.01+7*0.04+8*0.13+9*0.03+10*0.08+11*0.07+12*0.06=5.86
(2)如果按照查找概率高低进行重新排列之后的查找表应该为
33
66
45
34
65
90
35
82
12
64
78
56
0.23
0.13
0.12
0.1
0.09
0.08
0.07
0.06
0.04
0.04
0.03
0.01
ASL=1*0.23+2*0.13+3*0.12+4*0.1+5*0.09+6*0.08+7*0.07+8*0.06+9*0.04+10*0.04+11*0.03+12*0.01=4.36
8.5设计一个算法实现有序表的插入,要求利用二分法查找插入位置。
typedefstruct
{KeyTypekey;
DataTypeother;
}RecType;
RecTypeR[N+1];
intbinInsert(RecTypeR[],RecTypex)
/*向有序表中插入记录x*/
{intlow,hign,mid;
intt;
low=1;hign=N;
while(low<=hign)
{mid=(low+hign)/2;
if(x.key==R[mid].key)return0;/*记录已经存在,插入失败*/
elseif(x.keyelseif(x.key>R[mid].key)low=mid+1;
}
for(t=N;t>=low;t--)R[t+1]=R[t];/*向后移动记录*/
R[low]=x;/*插入记录x*/
return1;
}
8.6已知有一组关键字序列为:
36,75,83,54,12,50,62,30,67,92。
(1)画出依次插入各关键字生成的二叉排序树,并求出平均查找长度。
(2)画出依次插入各关键字构造AVL树的过程,对生成的平衡二叉排序树,并求出平均查找长度。
(1)二叉排序树,ASL=(1+2*2+3*3+4*3+5*1)/10=3.1
(2)AVL树的构造过程:
ASL=(1+2*2+3*4+4*3)/10=2.9
8.7从空树开始,画出按下列序列:
20,30,12,50,67,70,40,86,45,76,6,54依次插入关键字生成一棵3阶的B-树的过程。
若在生成的B-树上,依次删除关键字30,50,67,86,45,试画出删除每一个关键字后的B-树。
(1)依次插入关键字20,30,12,50,67,70,40,86,45,76,6,54生成3阶的B-树的过程
依次删除关键字30,50,67,86,45,的B-树:
8.8已知有一组关键字序列为:
19,01,13,23,24,55,20,84,27,68,11,10,77,散列函数为:
H(key)=key%13,散列空间的长度为19。
按照给出的关键字序列,试画出采用线性探测法和链地址法处理冲突时构造的散列表,并求出在等概率情况下,这两种方法的查找成功和查找不成功的平均查找长度。
解题:
(1)线性探测法解决冲突构造散列表的地址计算公式为:
d1=H(K)dj+1=(dj+1)%m,j=1,2,3,……m=19
H(19)=19%13=6H
(1)=1%13=1H(13)=13%13=0H(23)=23%13=10
H(24)=24%13=11H(55)=55%13=3H(20)=20%13=7
H(84)=84%13=6(冲突)H(84)=(6+1)%19=7(仍冲突)H(84)=(7+1)%19=8
H(27)=27%13=1(冲突)H(27)=(1+1)%19=2
H(68)=68%13=3(冲突)H(68)=(3+1)%19=4
H(11)=11%13=11(冲突)H(11)=(11+1)%19=12
H(10)=10%13=10(冲突)H(10)=(10+1)%19=11(仍冲突)H(10)=(11+1)%19=12(仍冲突)
H(10)=(12+1)%19=13
H(77)=77%13=12(冲突)H(77)=(12+1)%19=13(仍冲突)H(77)=(13+1)%19=14
构造的散列表如下:
ASL=(1+1+2+1+2+1+1+3+1+1+2+4+3)/13=23/13
(2)链地址法处理冲突时构造的散列表(链表中结点的插入采用头插法)
H(19)=19%13=6H
(1)=1%13=1H(13)=13%13=0H(23)=23%13=10
H(24)=24%13=11H(55)=55%13=3H(20)=20%13=7H(84)=84%13=6
H(27)=27%13=1H(68)=68%13=3H(11)=11%13=11H(10)=10%13=10
H(77)=77%13=12
ASL=(8+5*2)/13=18/13
8.9分别设计用链地址法和线性探测法解决冲突的散列表上插入和删除一个记录的算法。
解题:
(1)链地址法插入和删除记录的算法
typedefstruct/*定义记录的类型*/
{intkey;/*定义记录的关键字*/
OtherTypeother;/*记录的其他数据项*/
}RecType;
typedefstructnode/*定义存储记录的单链表*/
{RecTypedata;
structnode*next;
}Lnode;
intinsert(Lnode*h[],RecTypeitem)/*向散列表中插入一个记录item*/
{intd;
Lnode*p;
d=hash(item.key);/*使用hash()函数计算新记录的散列地址*/
p=(Lnode*)malloc(sizeof(RecType));/*为新记录分配存储空间*/
if(p==NULL)return0;
p->data=item;
p->next=h[d];/*将新记录插入到对应单链表的表头*/
h[d]=p;
return1;/*插入成功返回1*/
}
intdele(Lnode*h[],intk)/*在散列表中删除关键字为k的记录*/
{intd;
Lnode*p,*q;
d=hash(k);/*使用hash()函数计算散列地址*/
p=h[d];/*得到对应单链表的表头指针*/
if(!
p)return0;/*若单链表为空,返回0,说明删除失败*/
if(p->data.key==k)/*若删除的记录是表头结点,删除它并返回1*/
{h[d]=p->next;
free(p);
return1;
}
q=p->next;/*若删除的记录是非表头结点,在单链中查找被删除的记录*/
while(q)
{if(q->data.ke