中南大学数据结构与算法第10章内部排序课后作业答案.docx

上传人:b****3 文档编号:24899887 上传时间:2023-06-02 格式:DOCX 页数:20 大小:24.74KB
下载 相关 举报
中南大学数据结构与算法第10章内部排序课后作业答案.docx_第1页
第1页 / 共20页
中南大学数据结构与算法第10章内部排序课后作业答案.docx_第2页
第2页 / 共20页
中南大学数据结构与算法第10章内部排序课后作业答案.docx_第3页
第3页 / 共20页
中南大学数据结构与算法第10章内部排序课后作业答案.docx_第4页
第4页 / 共20页
中南大学数据结构与算法第10章内部排序课后作业答案.docx_第5页
第5页 / 共20页
点击查看更多>>
下载资源
资源描述

中南大学数据结构与算法第10章内部排序课后作业答案.docx

《中南大学数据结构与算法第10章内部排序课后作业答案.docx》由会员分享,可在线阅读,更多相关《中南大学数据结构与算法第10章内部排序课后作业答案.docx(20页珍藏版)》请在冰豆网上搜索。

中南大学数据结构与算法第10章内部排序课后作业答案.docx

中南大学数据结构与算法第10章内部排序课后作业答案

第10章内部排序习题练习答案

1.以关键字序列(265,301,751,129,937,863,742,694,076,438)为例,分别写出执行以下排序算法的各趟排序结束时,关键字序列的状态。

 

(1)直接插入排序

(2)希尔排序(3)冒泡排序(4)快速排序

 (5)直接选择排序(6)堆排序(7)归并排序(8)基数排序

  上述方法中,哪些是稳定的排序?

哪些是非稳定的排序?

对不稳定的排序试举出一个不稳定的实例。

答:

 

(1)直接插入排序:

(方括号表示无序区)

  初始态:

265[301751129937863742694076438]

  第一趟:

265301[751129937863742694076438]

  第二趟:

265301751[129937863742694076438]

  第三趟:

129265301751[937863742694076438]

  第四趟:

129265301751937[863742694076438]

  第五趟:

129265301751863937[742694076438]

  第六趟:

129265301742751863937[694076438]

  第七趟:

129265301694742751863937[076438]

  第八趟:

076129265301694742751863937[438]

  第九趟:

076129265301438694742751863937 

 

(2)希尔排序(增量为5,3,1)

  初始态:

265301751129937863742694076438

  第一趟:

265301694076438863742751129937 

  第二趟:

076301129265438694742751863937 

  第三趟:

076129265301438694742751863937 

 (3)冒泡排序(方括号为无序区)

  初始态[265301751129937863742694076438]

  第一趟:

076[265301751129937863742694438]

  第二趟:

076129[265301751438937863742694]

  第三趟:

076129265[301438694751937863742]

  第四趟:

076129265301[438694742751937863]

  第五趟:

076129265301438[694742751863937]

  第六趟:

076129265301438694742751863937

 (4)快速排序:

(方括号表示无序区,层表示对应的递归树的层数)

   初始态:

[265301751129937863742694076438]

  第二层:

[076129]265[751937863742694301438]

  第三层:

076[129]265[438301694742]751[863937]

  第四层:

076129265[301]438[694742]751863[937]

  第五层:

076129265301438694[742]751863937

  第六层:

076129265301438694742751863937

 (5)直接选择排序:

(方括号为无序区)

  初始态 [265301751129937863742694076438]

  第一趟:

076[301751129937863742694265438]

  第二趟:

076129[751301937863742694265438]

  第三趟:

076129265[301937863742694751438]

  第四趟:

076129265301[937863742694751438]

  第五趟:

076129265301438[863742694751937]

  第六趟:

076129265301438694[742751863937]

  第七趟:

076129265301438694742[751863937]

  第八趟:

076129265301438694742751[937863]

  第九趟:

076129265301438694742751863937

 (6)堆排序:

(通过画二叉树可以一步步得出排序结果)

     初始态   [265301751129937863742694076438]

    建立初始堆:

[937694863265438751742129075301]

 第一次排序重建堆:

[863694751765438301742129075]937

 第二次排序重建堆:

[751694742265438301075129]863937

 第三次排序重建堆:

[742694301265438129075]751863937

 第四次排序重建堆:

[694438301265075129]742751863937

 第五次排序重建堆:

[438265301129075]694742751863937

 第六次排序重建堆:

[301265075129]438694742751863937

 第七次排序重建堆:

[265129075]301438694742751863937

 第八次排序重建堆:

[129075]265301438694742751863937

 第九次排序重建堆:

075129265301438694742751863937

 (7)归并排序(为了表示方便,采用自底向上的归并,方括号为有序区)

  初始态:

[265][301][751][129][937][863][742][694][076][438]

  第一趟:

[265301][129751][863937][694742][076438]

  第二趟:

[129265301751][694742863937][076438]

  第三趟:

[129265301694742751863937][076438]

  第四趟:

[076129265301438694742751863937]

 (8)基数排序(方括号内表示一个箱子共有10个箱子,箱号从0到9)

   初始态:

265301751129937863742694076438

   第一趟:

[][301751][742][863][694][265][076][937][438][129]

  第二趟:

[301][][129][937438][742][751][863265][076][][694]

  第三趟:

[075][129][265][301][438][][694][742751][863][937] 

  在上面的排序方法中,直接插入排序、冒泡排序、归并排序和基数排序是稳定的,其他排序算法均是不稳定的,现举实例如下:

以带*号的表示区别。

  希尔排序:

[8,1,10,5,6,*8]

  快速排序:

[2,*2,1]

  直接选择排序:

[2,*2,1]

  堆排序:

[2,*2,1]

2.上题的排序方法中,哪些易于在链表(包括各种单、双、循环链表)上实现?

 

答:

  上题的排序方法中,直接插入排序、冒泡排序、直接选择排序、基数排序和归并排序等方法易于在链表上实现。

3.当R[low..high]中的关键字均相同时,Partion返回值是什么?

此时快速排序的的运行时间是多少?

能否修改Partion,使得划分结果是平衡的(即划分后左右区间的长度大致相等)?

 

答:

  此时Partion返回值是low.此时快速排序的运行时间是

 (high-low)(high-low-1)/2=O((high-low)^2),可以修改Partion,将其中RecTypepivot=R[i];句改为:

RecTypepivot=R[(j+i)/2];也就是取中间的关键字为基准,这样就能使划分的结果是平衡的。

4.若文件初态是反序的,则直接插入,直接选择和冒泡排序哪一个更好?

 

答:

  应选直接选择排序为更好。

分析如下:

 

(1)在直接插入排序算法中,反序输入时是最坏情况,此时:

  关键字的比较次数:

Cmax=(n+2)(n-2)/2;

    记录移动次数为:

Mmax=(n-1)(n+4)/2;

       Tmax=n^2-4n-3(以上二者相加)

 

(2)在冒泡排序算法中,反序也是最坏情况,此时:

    Cmax=n(n-1)/2;Mmax=3n(n-1)/2

    Tmax=2n^2-2n 

 (3)在选择排序算法中,

    Cmax=n(n-1)/2Mmax=3(n-1)

    Tmax=n^2/2-5n/2-3 

   由此可见,虽然它们的时间复杂度都是O(n^2),但是选择排序的常数因子为1/2,因此选择排序最省时间。

5.若文件初态是反序的,且要求输入稳定,则在直接插入、直接选择、冒泡和快速排序中就选选哪种方法为宜?

答:

   这四种排序算法中,直接选择、快速排序均是不稳定的,因此先予以排除,剩下两种算法中,由于直接插入算法所费时间比冒泡法更少(见上题分析),因此选择直接排序算法为宜。

6.有序数组是堆吗?

答:

   有序数组是堆。

因为有序数组中的关键字序列满足堆的性质。

若数组为降序,则此堆为大根堆,反之为小根堆。

7.高度为h的堆中,最多有多少个元素?

最少有多少个元素?

在大根堆中,关键字最小的元素可能存放在堆的哪些地方?

 

答:

  高度为h的堆实际上为一棵高度为h的完全二叉树,因此根据二叉树的性质可以算出,它最少应有2h-1个元素;最多可有2h-1个元素(注意一个有括号,一个没有)。

在大根堆中,关键字最小的元素可能存放在堆的任一叶子结点上。

8.判别下列序列是否为堆(小根堆或大根堆),若不是,则将其调整为堆:

 

(1)(100,86,73,35,39,42,57,66,21);

 

(2)(12,70,33,65,24,56,48,92,86,33);

 (3)(103,97,56,38,66,23,42,12,30,52,06,20);

 (4)(05,56,20,23,40,38,29,61,35,76,28,100).

答:

  堆的性质是:

任一非叶结点上的关键字均不大于(或不小于)其孩子结点上的关键字。

据此我们可以通过画二叉树来进行判断和调整:

 

(1)此序列是大根堆。

 

(2)此序列不是堆,经调整后成为小根堆:

   (12,24,33,65,33,56,48,92,86,70)

 (3)此序列是一大根堆。

 (4)此序列不是堆,经调整后成为小根堆:

   (01,05,20,23,28,38,29,56,35,76,40,100)

9.将两个长度为n的有序表归并为一个长度为2n的有序表,最小需要比较n次,最多需要比较2n-1次,请说明这两种情况发生时,两个被归并的表有何特征?

 

答:

  前一种情况下,这两个被归并的表中其中一个表的最大关键字不大于另一表中最小的关键字,也就是说,两个有序表是直接可以连接为有序的,因此,只需比较n次就可将一个表中元素转移完毕,另一个表全部照搬就行了。

  另一种情况下,是两个被归并的有序表中关键字序列完全一样,这时就要按次序轮流取其元素归并,因此比较次数达到2n-1.

10.设关键字序列为(0.79,0.13,0.16,0.64,0.39,0.20,0.89,0.53,0.71,0.42),给出桶排序的结果。

答:

 桶排序的结果如图:

 B[0..9]

 ┌──┐

 0│∧│

 ├──┤

 1│ →0.13→0.16→∧

 ├──┤

 2│ →0.20→∧

 ├──┤

 3│  →0.39→∧

  ├──┤

 4│  →0.42→∧

  ├──┤

 5│  →0.53→∧

  ├──┤

 6│  →0.64→∧

  ├──┤

 7│  →0.71→0.79→∧

  ├──┤

 8│  →0.89→∧

  ├──┤

 9│∧│

  └──┘

   结果为:

0.130.160.200.390.420.530.640.710.790.89

11.若关键字是非负整数、快速排序、归并、堆和基数排序啊一个最快?

若要求辅助空间为O

(1),则应选择谁?

若要求排序是稳定的,且关键字是实数,则应选择谁?

答:

   若关键字是非负整数,则基数排序最快;若要求辅助空间为O

(1),则应选堆排序;若要求排序是稳定的,且关键字是实数,则应选归并排序,因为基数排序不适用于实数,虽然它也是稳定的。

12.对于8.7节的表8.2,解释下述问题:

 

(1)当待排序的关键字序列的初始态分别为正序和反序时,为什么直接选择排序的时间基本相同?

若采用本书8.4.1节的算法,这两种情况下的排序时间是否基本相同?

 

(2)为什么数组的初态为正序时,冒泡和直接插入排序的执行时间最少?

 (3)若采用8.3.2节的快速排序,则数组初态为正序和反序时,能得到与表8.2类似的结果吗?

答:

 

(1)由于在直接选择排序中,主要的操作是比较操作和移动操作。

无论文件的初始状态如何,若文件有n个记录,则都要进行n-1趟直接选择排序,第i趟直接选择排序中都要做n-i次比较才能选出最小关键字的记录。

所以总的比较次数都为O(n2)。

至于记录的移动次数,初始文件为正序时,移动次数为0,当文件初始时为反序,总的移动次数为3(n-1)。

因此当待排序的关键字序列的初始态分别为正序和反序时,直接选择排序的时间基本相同,为O(n2)。

若采用本书8.4.1节的算法,这两种情况下的排序时间基本相同。

 

(2)当冒泡排序是正序时,只需做一趟冒泡排序就可完成,共做n-1次比较,移动次数为0,所以执行时间最少。

而直接插入排序时,若初始为正序,则做了n-1趟直接插入排序,但每趟排序只做了一次比较,共做n-1次比较。

移动次数为0。

所以当数组初态为正序,直接插入排序时间也最少。

 (3)不能,其中辅助空间不同

13.将哨兵放在R[n]中,被排序的记录放在R[0..n-1]中,重写直接插入排序算法。

解:

   重写的算法如下:

 voidInsertSort(SeqListR)

  {//对顺序表中记录R[0..n-1]按递增序进行插入排序

   inti,j;

   for(i=n-2;i>=0;i--)//在有序区中依次插入R[n-2]..R[0]

    if(R[i].key>R[i+1].key)//若不是这样则R[i]原位不动

     { 

      R[n]=R[i];j=i+1;//R[n]是哨兵

      do{//从左向右在有序区中查找插入位置

        R[j-1]=R[j];//将关键字小于R[i].key的记录向右移

        j++;

       }while(R[j].key

      R[j-1]=R[n];//将R[i]插入到正确位置上

     }//endif

  }//InsertSort.

14.以单链表作为存储结构实现直接插入排序算法。

 

解:

 #defineintKeyType//定义KeyType为int型

 typedefstructnode{

   KeyTypekey;//关键字域

   OtherInfoTypeinfo;//其它信息域,

   structnode*next;//链表中指针域

  }RecNode;//记录结点类型

 typedefRecNode*LinkList;//单链表用LinkList表示

 voidInsertSort(LinkListhead)

  {//链式存储结构的直接插入排序算法,head是带头结点的单链表

   RecNode*p,*q,*s; 

   if((head->next)&&(head->next->next))//当表中含有结点数大于1

    {

     p=head->next->next;//p指向第二个节点

     head->next=NULL;

     q=head;//指向插入位置的前驱节点

     while(p)&&(q->next)&&(p->keynext->key)

      q=q->next;

     if(p)

      {s=p;p=p->next;//将要插入结点摘下

       s->next=q->next;//插入合适位置:

q结点后

       q->next=s;

      }

    }

  }

15.设计一算法,使得在尽可能少的时间内重排数组,将所有取负值的关键字放在所有取非负值的关键字之前。

请分析算法的时间复杂度。

 

解:

  因为只需将负数关键字排在前面而无需进行精确排列顺序,因此本算法采用两端扫描的方法,就象快速排序采用的方法一样,左边扫描到正数时停止,开始扫描右边,遇到负数时与左边的当前记录交换,如此交替进行,一趟下来就可以完成排序。

 voidReSort(SeqListR)

  {//重排数组,使负值关键字在前

   inti=1,j=n;//数组存放在R[1..n]中

   while(i

    {while(i

      i++;

     R[0]=R[i];//R[0]为辅助空间

     while(i=0)//遇到正数则继续向左扫描

        j--;

     R[i++]=R[j];R[j--]=R[0];//交换当前两个元素并移动指针

    }//endwhile

  }//ReSort

  本算法在任何情况下的比较次数均为n(每个元素和0)相比,交换次数少于n/2,总的来说,时间复杂度为O(n).

*16.写一个双向冒泡排序的算法,即在排序过程中交替改变扫描方向。

 

解:

  算法如下:

 voidBubbleSort(SeqListR)

  {//R[1..n]是待排序文件,双向扫描冒泡排序

   inti,j,k;

   Booleanexchange;//交换标记

   i=n;j=1;

   exchange=TRUE;

   while(i>j)&&(exchange)

    {k=i-1;exchange=FALSE;

     while(k>=j)//由下往上扫描

      {if(r[k]>r[k+1])

        {r[0]=r[k];r[k]=r[k+1];r[k+1]=r[k];exchange=TRUE;//交换

        }//endif

       k--;

      }//endwhile

     if(exchange)

      {exchange=FALSE;

       j++;k=j+1;

       while(k<=i)//由上往下扫描

        {if(r[k]

         {r[0]=r[k];r[k]=r[k-1];r[k-1]=r[k];exchange=TRUE;//交换

         }//endif

         k++;

        }endwhile

       i--;

      }//endif

    }endwhile

  }//endsort

17.下面是一个自上往下扫描的冒泡排序的伪代码算法,它采用lastExchange来记录每趟扫描中进行交换的最后一个元素的位置,并以它作为下一趟排序循环终止的控制值。

请仿照它写一个自下往上扫描的冒泡排序算法。

voidBubbleSort(intA[],intn)

 //不妨设A[0..n-1]是整型向量

 intlastExchange,j,i=n-1;

 while(i>0)

  lastExchange=0;

  for(j=0;j

    if(A[j+1]

      交换A[j]和A[j+1];

      lastExchange=j;

    }

  i=lastExchange;//将i置为最后交换的位置

 }//endwhile

}//BubbleSort 

解:

算法如下:

voidBubbleSort(intA[],intn)

 //不妨设A[0..n-1]是整型向量

 intlastExchange,j,i=0;

 while(i

  lastExchange=n;

  for(j=n-1;j>i;j--)//从下往上扫描A[0..i]

    if(A[j-1]

      交换A[j]和A[j-1];

      lastExchange=j;

    }

  i=lastExchange;//将i置为最后交换的位置

 }//endwhile

}//BubbleSort

18.改写快速排序算法,要求采用三者取中的方式选择划分的基准记录;若当前被排序的区间长度小于等于3时,无须划分而是直接采用直接插入方式对其排序。

 

解:

  改写后的算法如下:

 void

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 考试认证 > 财会金融考试

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1