试问如何进行才能使所作的关键字间比较次数达到最小?
9.5 插入排序中找插入位置的操作可以通过二分查找的方法来实现。
试据此写一个改进后的插入排序算法。
9.6 编写一个双向起泡的排序算法,即相邻两遍向相反方向起泡。
9.7编写算法,对n个关键字取整数值的记录序列进行整理,以使所有关键字为负值的记录排在关键字为非负值的记录之前,要求:
采取顺序存储结构,至多使用一个记录的辅助存储空间;
算法的时间复杂度O(n);
讨论算法中记录的最大移动次数。
9.8试以单链表为存储结构实现简单选择排序的算法
9.9假设含n个记录的序列中,其所有关键字为值介于v和w之间的整数,且其中很多关键字的值是相同的。
则可按如下方法排序:
另设数组number[v...w]且令number[i]为统计关键字取整数I的记录数,之后按number重排序列以达到有序,编写算法实现上述排序方法,并讨论此方法的优缺点。
9.10已知两个有序序列(a1,a2,...,am)和(am+1,am+2,...,an),并且其中一个序列的记录个数少于s,且s=?
√n?
. 试写一个算法,用O(n)时间和O
(1)附加空间完成这两个有序序列的归并。
9.11偶交换排序如下所述:
第一趟对所有奇数i,将a[i]和a[i+1]进行比较;第二趟对所有偶数i,将a[i]和a[i+1]进行比较,若a[i]>a[i+1],则将两者交换;第一趟对所有奇数i,第二趟对所有偶数i,…,依次类推直至整个序列有序为止。
(1)这种排序方法的结束条件是什么?
(2)分析当初始序列为正序或逆序两种情况下,奇偶交换排序过程中所需进行的关键字比较的次数。
(3)写出奇偶交换排序的算法。
9.12设计一个用链表表示的直接选择排序算法。
9.13插入排序中找插入位置的操作可以通过二分查找的方法来实现。
试据此写一个改进后的插入排序算法。
9.14一个线性表中的元素为正整数或负整数。
设计一个算法,将正整数和负整数分开,使线性表的前一半为负整数,后一半为正整数。
不要求对元素排序,但要尽量减少交换次数。
9.15为什么通常使用一维数组作为堆的存放形式?
9.16已知(k1,k2,…,kn)是堆,写一个算法将(k1,k2,…,kn,kn+1)调整为堆。
按此思想写一个从空堆开始一个一个添入元素的建堆算法。
9.17试比较直接插入排序、简单选择排序、快速排序、堆排序、归并排序、希尔排序和基数排序的时空性能、稳定性和适用情况。
9.18 在供选择的答案中填入正确答案:
(1)、排序(分类)的方法有许多种:
__A__法从未排序序列中依次取出元素,与排序序列(初始为空)中的元素作比较,将其放入已排序列的正确位置上;__B__法从未排序序列中挑选元素,并将其依次放入已排序序(初始时为空)的一端;交换排序法是对序列中元素进行一系列的比较,当被比较的两元素逆序时进行交换。
__C___和__D__是基于这类方法的两种排序方法,而__D__是比__C__效率更高的方法,利用某种算法,根据元素的关键值计算出排序位置的方法是__E__。
供选择答案
① 选择排序 ② 快速排序 ③ 插入排序 ④ 冒泡排序
⑤ 归并排序 ⑥ 二分排序 ⑦ 哈希排序 ⑧ 基数排序
(2)、一组记录的关键字为(46,79,56,38,40,84),利用快速排序的方法,以第一个记录为基准得到的一次划分结果为 。
A、38,40,46,56,79,84
B、40,38,46,79,56,84
C、40,38,46,56,79,84
D、40,38,46,84,56,79
(3)、下列排序算法中, 算法可能会出现下面情况:
初始数据有序时,花费时间反而最多。
A、堆排序 B、冒泡排序 C、快速排序 D、SHELL排序
9.19 判断正误:
()在一个大堆中,最小元素不一定在最后。
()对n个记录采用快速排序方法进行排序,最坏情况下所需时间是o(nlog2n)。
()在执行某排序算法过程中,出现了排序码朝着与最终排序序列相反方向移动的现象,则称该算法是不稳定的。
实习题
一、随机生成30个数,试比较直接插入排序、简单选择排序、起泡排序、快速排序、堆排序和希尔排序的时空性能和稳定性。
二、统计成绩。
给出n个学生的考试成绩表,每条信息由姓名与分数组成。
(1)按分数高低次序,打印出每个学生在考试中获得的名次,分数相同的为同一名次;
(2)按名次列出每个学生的姓名与分数。
9.1
(1)无序表:
顺序查找不成功时,查找长度为n+1;成功时,平均查找长度为1/(n+1)*(1+2+…+(n+1))=(n+2)/2;两者不相同。
(2)表中只有一个关键字等于给定值k的记录,无序表、有序表:
顺序查找成功时,平均查找长度均为1/(n)*(1+2+…+n)=(n+1)/2;两者相同。
(3)表中只有m个关键字等于给定值k的记录,无序表:
ASL=n+1;有序表:
ASL=(n+1)/2+m;两者不相同。
9.3
ASL=1/10(1+2*2+4*3+3*4)=2.9
9.11
9.14
删除50后
删除68后
9.19
22
67
41
30
53
46
13
01
0
1
2
3
4
5
6
7
8
9
10
ASL=(4*1+2*2+3+6)/8=17/8
9.25
intSearch-Seq(SSTableST,KeyTypekey){
//在顺序表ST中顺序查找其关键字等于key的数据元素,ST按关键字自大至小有序,
//若找到,则函数值为该元素在表中的位置,否则为0
ST.elem[ST.length+1].key=key;
for(i=1;ST.elem[i].key>key;++i);
if(ST.elem[i].key==key)&&(i<=ST.length)returni
elsereturn0;
}//Search-Seq
9.31
TelemTypeMaxv(BitreeT){
//返回二叉排序树T中所有结点的最大值
for(p=T;p->rchild;p=p->rchild);
returnp->data;
}//Maxv
TelemTypeMinv(BitreeT){
//返回二叉排序树T中所有结点的最小值
for(p=T;p->lchild;p=p->lchild);
returnp->data;
}//Minv
StatusIsBST(BitreeT){
//判别T是否为二叉排序树
if(!
T)returnOK;
elseif((!
T->lchild)||((T->lchild)&&(IsBST(T->lchild)&&(Maxv(T->lchild)data)))
&&((!
T->rchild)||((T->rchild)&&(IsBST(T->rchild)&&(Minv(T->rchild)>T->data)))
returnOK
elsereturnERROR;
}//IsBST
9.33
StatusOutputGEx(BitreeT,TelemTypex){
//从大到小输出给定二叉排序树T中所有值不小于x的数据元素
if(T){
if(OutputGEx(T->rchild,x))
if(T->data>=x){
print(T->data);
if(OutputGEx(T->lchild,x))returnOK;
}
elsereturnOK;
}
elsereturnOK;
}//OutputGEx
第九章查找
9.25
intSearch_Sq(SSTableST,intkey)//在有序表上顺序查找的算法,监视哨设在高下标端
{
ST.elem[ST.length+1].key=key;
for(i=1;ST.elem[i].key>key;i++);
if(i>ST.length||ST.elem[i].key returni;
}//Search_Sq
分析:
本算法查找成功情况下的平均查找长度为ST.length/2,不成功情况下为ST.length.
9.26
intSearch_Bin_Digui(SSTableST,intkey,intlow,inthigh)//折半查找的递归算法
{
if(low>high)return0;//查找不到时返回0
mid=(low+high)/2;
if(ST.elem[mid].key==key)returnmid;
elseif(ST.elem[mid].key>key)
returnSearch_Bin_Digui(ST,key,low,mid-1);
elsereturnSearch_Bin_Digui(ST,key,mid+1,high);
}
}//Search_Bin_Digui
9.27
intLocate_Bin(SSTableST,intkey)//折半查找,返回小于或等于待查元素的最后一个结点号
{
int*r;
r=ST.elem;
if(key elseif(key>=r[ST.length].key)returnST.length;
low=1;high=ST.length;
while(low<=high)
{
mid=(low+high)/2;
if(key>=r[mid].key&&key returnmid;
elseif(key elselow=mid;
}//本算法不存在查找失败的情况,不需要return0;
}//Locate_Bin
9.28
typedefstruct{
intmaxkey;
intfirstloc;
}Index;
typedefstruct{
int*elem;
intlength;
Indexidx[MAXBLOCK];//每块起始位置和最大元素,其中idx[0]不利用,其内容初始化为{0,0}以利于折半查找
intblknum;//块的数目
}IdxSqList;//索引顺序表类型
intSearch_IdxSeq(IdxSqListL,intkey)//分块查找,用折半查找法确定记录所在块,块内采用顺序查找法
{
if(key>L.idx[L.blknum].maxkey)returnERROR;//超过最大元素
low=1;high=L.blknum;
found=0;
while(low<=high&&!
found)//折半查找记录所在块号mid
{
mid=(low+high)/2;
if(key<=L.idx[mid].maxkey&&key>L.idx[mid-1].maxkey)
found=1;
elseif(key>L.idx[mid].maxkey)
low=mid+1;
elsehigh=mid-1;
}
i=L.idx[mid].firstloc;//块的下界
j=i+blksize-1;//块的上界
temp=L.elem[i-1];//保存相邻元素
L.elem[i-1]=key;//设置监视哨
for(k=j;L.elem[k]!
=key;k--);//顺序查找
L.elem[i-1]=temp;//恢复元素
if(k
returnk;
}//Search_IdxSeq
分析:
在块内进行顺序查找时,如果需要设置监视哨,则必须先保存相邻块的相邻元素,以免数据丢失.
9.29
typedefstruct{
LNode*h;//h指向最小元素
LNode*t;//t指向上次查找的结点
}CSList;
LNode*Search_CSList(CSList&L,intkey)//在有序单循环链表存储结构上的查找算法,假定每次查找都成功
{
if(L.t->data==key)returnL.t;
elseif(L.t->data>key)
for(p=L.h,i=1;p->data!
=key;p=p->next,i++);
else
for(p=L.t,i=L.tpos;p->data!
=key;p=p->next,i++);
L.t=p;//更新t指针
returnp;
}//Search_CSList
分析:
由于题目中假定每次查找都是成功的,所以本算法中没有关于查找失败的处理.由微积分可得,在等概率情况下,平均查找长度约为n/3.
9.30
typedefstruct{
DLNode*pre;
intdata;
DLNode*next;
}DLNode;
typedefstruct{
DLNode*sp;
intlength;
}DSList;//供查找的双向循环链表类型
DLNode*Search_DSList(DSList&L,intkey)//在有序双向循环链表存储结构上的查找算法,假定每次查找都成功
{
p=L.sp;
if(p->data>key)
{
while(p->data>key)p=p->pre;
L.sp=p;
}
elseif(p->data {
while(p->datanext;
L.sp=p;
}
returnp;
}//Search_DSList
分析:
本题的平均查找长度与上一题相同,也是n/3.
9.31
intlast=0,flag=1;
intIs_BSTree(BitreeT)//判断二叉树T是否二叉排序树,是则返回1,否则返回0
{
if(T->lchild&&flag)Is_BSTree(T->lchild);
if(T->data last=T->data;
if(T->rchild&&flag)Is_BSTree(T->rchild);
returnflag;
}//Is_BSTree
9.32
intlast=0;
voidMaxLT_MinGT(BiTreeT,intx)//找到二叉排序树T中小于x的最大元素和大于x的最小元素
{
if(T->lchild)MaxLT_MinGT(T->lchild,x);//本算法仍是借助中序遍历来实现
if(lastdata>=x)//找到了小于x的最大元素
printf("a=%d\n",last);
if(last<=x&&T->data>x)//找到了大于x的最小元素
printf("b=%d\n",T->data);
last=T->data;
if(T->rchild)MaxLT_MinGT(T->rchild,x);
}//MaxLT_MinGT
9.33
voidPrint_NLT(BiTreeT,intx)//从大到小输出二叉排序树T中所有不小于x的元素
{
if(T->rchild)Print_NLT(T->rchild,x);
if(T->data printf("%d\n",T->data);
if(T->lchild)Print_NLT(T->lchild,x);//先右后左的中序遍历
}//Print_NLT
9.34
voidDelete_NLT(BiTree&T,intx)//删除二叉排序树T中所有不小于x元素结点,并释放空间
{
if(T->rchild)Delete_NLT(T->rchild,x);
if(T->data q=T;
T=T->lchild;
free(q);//如果树根不小于x,则删除树根,并以左子树的根作为新的树根
if(T)Delete_NLT(T,x);//继续在左子树中执行算法
}//Delete_NLT
9.35
voidPrint_Between(BiThrTreeT,inta,intb)//打印输出后继线索二叉排序树T中所有大于a且小于b的元素
{
p=T;
while(!
p->ltag)p=p->lchild;//找到最小元素
while(p&&p->data
{
if(p->data>a)printf("%d\n",p->data);//输出符合条件的元素
if(p->rtag)p=p->rtag;
else
{
p=p->rchild;
while(!
p->ltag)p=p->lchild;
}//转到中序后继
}//while
}//Print_Between
9.36
voidBSTree_Insert_Key(BiThrTree&T,intx)//在后继线索二叉排序树T中插入元素x
{
if(T->data {
if(T->rtag)//T没有右子树时,作为右孩子插入
{
p=T->rchild;
q=(BiThrNode*)malloc(sizeof(BiThrNode));
q->data=x;
T->rchild=q;T->rtag=0;
q->rtag=1;q->rchild=p;//修改原线索
}
elseBSTree_Insert_Key(T->rchild,x);//T有右子树时,插入右子树中
}//if
elseif(T->data>x)//插入到左子树中
{
if(!
T->lchild)//T没有左子树时,作为左孩子插入
{
q=(BiThrNode*)malloc(sizeof(BiThrNode));
q->data=x;
T->lchild=q;
q->rtag=1;q->rchild=T;//修改自身的线索
}
elseBSTree_Insert_Key(T->lchild,x);//T有左子树时,插入左子树中
}//if
}//BSTree_Insert_Key
9.37
StatusBSTree_Delete_key(BiThrTree&T,intx)//在后继线索二叉排序树T中删除元素x
{
BTNode*pre,*ptr,*suc;//ptr为x所在结点,pre和suc分别指向ptr的前驱和后继
p=T;last=NULL;//last始终指向当前结点p的前一个(前驱)
while(!
p->ltag)p=p->lchild;//找到中序起始元素
while(p)
{
if(p->data==x)//找到了元素x结点
{
pre=last;
ptr=p;
}
elseif(last&&last->data==x)suc=p;//找到了x的后继
if(p->rtag)p=p->rtag;
else
{
p=p->rchild;
while(!
p->ltag)p=p->lchild;
}//转到中序后继
last=p;
}//while//借助中序遍历找到元素x及其前驱和后继结点
if(!
ptr)returnERROR;//未找到待删结点
Delete_BSTree(ptr);//删除x结点
if(pre&&pre->rtag)
pre->rchild=suc;//修改线索
returnOK;
}//BSTree_Delete_key
voidDelete_BSTree(BiThrTree&T)//课本上给出的删除二叉排序树的子树T的算法,按照线索二叉树的结构作了一些改动
{
q=T;
if(!
T->ltag&&T->r