数据结构答案78章.docx
《数据结构答案78章.docx》由会员分享,可在线阅读,更多相关《数据结构答案78章.docx(15页珍藏版)》请在冰豆网上搜索。
数据结构答案78章
第7章排序
回答下列概念:
稳定排序、内排序
排序码相同的两个记录经过排序之后,其相对次序保持不变,称该排序方法是稳定的;反之,称该排序方法是不稳定的。
待排序的文件一般都保存在外存中,当文件比较小时,可将全部记录一次读入内存,使得整个排序过程全部在内存中进行,这种排序称为内部排序。
常用的实现排序的方法有几大类它们的实现思想是什么
插入排序(InsertionSort)的基本思想是:
将一个待排序记录按照排序码的大小插入到一个有序序列的适当位置,使得插入后的序列仍然有序,直到所有记录全部插入到有序序列中。
插入排序主要包括两种方法:
直接插入排序和希尔(Shell)排序。
交换排序的基本思想是:
两两比较待排序记录的排序码,不符合排列顺序则交换记录,直到所有记录的排序码都符合排序要求。
本节主要介绍两种交换排序:
起泡排序和快速排序。
选择排序(SelectionSort)的基本思想是:
每一次从待排序记录序列中选取一个排序码最小(或最大)的记录,放在待排序记录序列的最前面(或最后面),重复此过程,直到所有的记录按排序码排好序。
本节将介绍直接选择排序和堆排序两种方法。
归并排序(MergeSort)是利用“归并”技术实现的排序方法。
所谓归并就是将两个或多个有序表合并成一个有序表的过程。
如果是将两个有序表合并成一个有序表称为二路归并;同理,将三个有序表合并成一个有序表称为三路归并,以此类推可以有n路归并等。
但二路归并是最简单和最常用的。
本节主要讲二路归并技术实现的归并排序。
基数排序(RadixSort)是与前面各类排序方法完全不同的一种排序方法,它是基于排序码的结构分解,然后通过“分配”和“收集”方法实现的排序。
已知一组记录的排序码为(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(二趟分配收集结果)
是稳定的排序。
在堆排序、快速排序和归并排序中,
(1)若从存储空间考虑,应该首先选取哪个方法其次选取哪个方法最后选取哪个方法
(2)若从排序结果的稳定性考虑,则应该选取哪个方法(3)若只从平均情况下排序最快考虑,应该选取哪个方法(4)若只从最坏情况下排序最快并且要节省内存考虑,则应该选取哪个方法
(1)若从存储空间考虑,应该首先选取堆排序方法,其次选取快速排序方法,最后选取归并排序方法。
(2)若从排序结果的稳定性考虑,则应该选取归并排序。
(3)若只从平均情况下排序最快考虑,应该选取快速排序。
(4)若只从最坏情况下排序最快并且要节省内存考虑,则应该选取堆排序方法。
已知一组记录的排序码为(54,38,96,23,15,72,60,45,83),利用快速排序方法对其进行排序。
(1)递归调用使用的栈所能达到的最大深度是多少共需递归调用的次数是多少
(2)其中,第二次递归调用时是对哪一组记录进行快速排序
(1)递归调用使用的栈所能达到的最大深度是4,共需递归调用的次数是4次。
(2)其中,第二次递归调用时是对{72,60,96,83}进行快速排序。
设计单链表作存储结构实现直接插入排序和直接选择排序的算法,并分析你设计的算法的时间复杂度和空间复杂度。
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)n]进行双向冒泡排序。
即相邻两遍向两个相反方向起泡*/
{inti=1,exchange=1;eyr[j+1].key){t=r[j];r[j]=r[j+1];r[j+1]=t;exchange=1;}
ey)return0;/*记录已经存在,插入失败*/
elseifelseif>R[mid].key)low=mid+1;
}
for(t=N;t>=low;t--)R[t+1]=R[t];/*向后移动记录*/
R[low]=x;/*插入记录x*/
return1;
}
已知有一组关键字序列为:
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=
(2)AVL树的构造过程:
ASL=(1+2*2+3*4+4*3)/10=
从空树开始,画出按下列序列:
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-树:
已知有一组关键字序列为:
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
分别设计用链地址法和线性探测法解决冲突的散列表上插入和删除一个记录的算法。
解题:
(1)链地址法插入和删除记录的算法
typedefstruct/*定义记录的类型*/
{intkey;/*定义记录的关键字*/
OtherTypeother;/*记录的其他数据项*/
}RecType;
typedefstructnode/*定义存储记录的单链表*/
{RecTypedata;
structnode*next;
}Lnode;
intinsert(Lnode*h[],RecTypeitem)/*向散列表中插入一个记录item*/
{intd;
Lnode*p;
d=hash;/*使用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->==k)/*若删除的记录是表头结点,删除它并返回1*/
{h[d]=p->next;
free(p);
return1;
}
q=p->next;/*若删除的记录是非表头结点,在单链中查找被删除的记录*/
while(q)
{if(q->==k)/*找到被删除的记录,删除它并返回1*/
{p->next=q->next;
free(q);
return1;
}
else
{p=q;
q=q->next;
}
}
return0;/*返回0,说明删除失败*/
}
(2)线性探测法解决冲突的散列表中的插入和删除记录的算法
typedefstruct/*定义记录的类型*/
{intkey;/*定义记录的关键字*/
OtherTypeother;/*记录的其他数据项*/
}RecType;
RecTypeR[N];/*N为散列表的空间长度*/
intinsert(RecTypeR[],RecTypeitem)/*向散列表中插入一个记录item*/
{intd,temp;
d=hash;/*使用hash()函数计算新记录的散列地址*/
temp=d;
while(R[d].key!
=NullTag)/*NullTag是一个与关键字同类型的常量,是散列表置空的标记值*/
{d=(d+1)%N;
if(d==temp)return0;/*返回0表示散列表已满,插入失败*/
}
R[d]=item;
return1;/*插入成功返回1*/
}
intdele(RecTypeR[],intk)/*在散列表中删除关键字为k的记录*/
{intd,temp;
d=hash(k);/*使用hash()函数计算散列地址*/
temp=d;
while(R[d].key!
=NullTag)
{if(R[d].key==k){R[d].key=DeleteTag;/*DeleteTag是一个常量,标明散列表该位置删除记录可以再利用*/
return1;}
elsed=(d+1)%N;
if(d==temp)return0;/*查找一圈返回原处,表明没有该记录,删除失败返回0*/
}
return0;
}