1 / 145

排序在计算机软件系统设计中占有相当重要的地位 , 特别是在事务处理系统中 , 需要经常对有关的数据排序 , 以便提高检索等操作的效率。

第九章 排序. 排序在计算机软件系统设计中占有相当重要的地位 , 特别是在事务处理系统中 , 需要经常对有关的数据排序 , 以便提高检索等操作的效率。 本章将介绍一些常用的排序算法 , 并对有关的算法进行性能分析和对比。它们有 : 交换排序、插入排序、选择排序、归并排序和基数排序. 9.1 基础知识. 1 . 排序 : 设含有 n 个数据元素的数据表为 : {R[0] 、 R[1] 、 … 、 R[n-1]} 其相应的关键字序列为 : {K[0] 、 K[1] 、 … 、 K[n-1]}

trung
Download Presentation

排序在计算机软件系统设计中占有相当重要的地位 , 特别是在事务处理系统中 , 需要经常对有关的数据排序 , 以便提高检索等操作的效率。

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第九章 排序 排序在计算机软件系统设计中占有相当重要的地位,特别是在事务处理系统中,需要经常对有关的数据排序,以便提高检索等操作的效率。 本章将介绍一些常用的排序算法,并对有关的算法进行性能分析和对比。它们有: 交换排序、插入排序、选择排序、归并排序和基数排序

  2. 9.1 基础知识 1.排序: 设含有n个数据元素的数据表为: {R[0]、R[1]、…、R[n-1]} 其相应的关键字序列为: {K[0]、K[1]、…、K[n-1]} 若这些数据元素已按各关键字的递增(或递减)次序排列,则称该数据表是已排序的。 那么把一个数据表变成已排序的数据表的过程就叫排序。

  3. 姓名 年龄 体重 1李由 54 62 2王天 57 76 3七大 24 75 4张强 24 72 5陈华 24 53 姓名 年龄 体重 3七大 24 75 4张强 24 72 5陈华 24 53 1李由 54 62 2王天 57 76 例如: 上表按年龄无序,如果按关键字年龄用某方法排序后可以得到下表,则按年龄是有序的:

  4. 2.排序的稳定性: 假设在排序表中有任意两个i≠j的数据元素R[i]和R[j],它们的关键字K[i]= =K[j],且在排序之前,数据元素R[i]排在R[j]的前面。 那么若在排序之后,数据元素R[i]仍在数据元素R[j]的前面,则称这种排序方法是稳定的,否则称这种排序方法是不稳定的。 也就是说如果关键字相同的数据元素在排序前、后的相对位置没有改变,则称这种排序方法是稳定的,否则称这种排序方法是不稳定的。 请见下页举例:

  5. 姓名 年龄 体重 1李由 54 62 2王天 57 76 3七大 24 75 4张强 24 72 5陈华 24 53 姓名 年龄 体重 3七大 24 75 4张强 24 72 5陈华 24 53 1李由 54 62 2王天 57 76 上表按年龄无序,如果按关键字年龄用某方法排序后得到下表: 注意反色的三条记录保持原有排列顺序,则称该排序方法是稳定的!

  6. 姓名 年龄 体重 1李由 54 62 2王天 57 76 姓名 年龄 体重 3七大 24 75 4张强 24 72 4张强 24 72 3七大 24 75 5陈华 24 53 5陈华 24 53 1李由 54 62 2王天 57 76 如果用另一方法排序后得到下表: 原3,4,5记录顺序改变,则称该排序方法是不稳定的!

  7. 3.内部排序与外部排序:根据在排序过程中数据元素是否全部在内存中进行,排序可分为两大类:内部排序和外部排序。3.内部排序与外部排序:根据在排序过程中数据元素是否全部在内存中进行,排序可分为两大类:内部排序和外部排序。 内部排序是指在排序过程中数据元素全部存放在内存进行的排序; 外部排序是指由于数据元素太多,在排序期间全部数据元素不能同时存放在内存中,必须在排序过程中,不断地在内存和外存之间交换数据元素的排序。 4.排序的效率:排序算法的效率可以从时间复杂度和空间复杂度两个方面来分析。即: • 关键字的比较次数 • 数据元素的移动次数 • 辅助空间大小 时间复杂度 空间复杂度

  8. 5.排序表的抽象数据类型描述 见教材P293-P295 6. 用顺序表表示的排序表的类定义: constint MaxSize = 100; template <class Type> class sortlist; //排序表 template <class Type> class element { //数据元素的类定义 friendclass sortlist <Type>; private: Type key;//数据元素的关键字 other; //其它信息

  9. 6.用顺序表表示的排序表的类定义: public://数据元素的类定义 element ( ) ; //构造函数 Type getKey ( ) { return key; } //取数据元素关键字 void setKey ( const Type k ) { key = k; } //修改数据元素关键字 element<Type> & operator=(element<Type> &x) { this = x; } int operator = = ( Type & k ) { return ! ( key < k || k < key ); } int operator != ( Type & k ) { return ( key < k || k < key );} int operator <= ( Type & k ) { return ! ( key > k ); } int operator >= ( Type & k ) { return ! ( key < k ); } int operator < ( Type & k ) { return ( key < k ); } int operator > ( Type & k ) { return ( key > k ); } }//class element 数据元素的类定义

  10. 6.用顺序表表示的排序表的类定义: template <class Type> class sortlist {//排序表的类定义 private: element <Type> * Arr; //存储数据元素的向量(排序表) int CurrentSize; //数据表中数据元素的个数 public: sortlist ( ) : CurrentSize (0) { Arr = new element <Type> [MaxSize]; } //构造函数 sortlist ( ) { delete Arr [ ]}; //析构函数 void swap ( element <Type> & x, element <Type> & y ) //数据元素x和y交换位置 { element<Type> temp = x; x = y; y = temp; } } //class sortlist 排序表的类定义

  11. 9.2 交换排序 交换排序的基本思想是两两比较待排序对象的关键字,如果发生逆序(即排列顺序与排序后的次序正好相反),则交换之,直到所有对象都排好序为止。 在本节中,将介绍两种交换排序的方法: 冒泡排序和快速排序。

  12. 9.2.1 冒泡排序 排序过程:设待排序对象序列中的对象个数为 n • 将第1个记录的关键字与第2个记录的关键字进行比较,若为逆序,即Arr[0].Key> Arr[1].Key,则交换; • 然后比较第2个记录与第3个记录; • 依次类推; • 直至第n-1个记录和第n个记录比较为止 • 这是第一趟冒泡排序,其结果是: • 关键字最大的记录被安置在最后一个记录位置上 • 对前n-1个记录进行第二趟冒泡排序,结果使关键字次大的记录被安置在第n-1个记录位置上

  13. 9.2.1 冒泡排序 排序过程:设待排序对象序列中的对象个数为 n • 将第1个记录的关键字与第2个记录的关键字进行比较,若为逆序,即Arr[0].Key> Arr[1].Key,则交换; • 然后比较第2个记录与第3个记录; • 依次类推; • 直至第n-1个记录和第n个记录比较为止 • 这是第一趟冒泡排序,其结果是: • 关键字最大的记录被安置在最后一个记录位置上 • 对前n-1个记录进行第二趟冒泡排序,结果使关键字次大的记录被安置在第n-1个记录位置上 • 重复上述过程,直到"在一趟排序过程中没有进行过交换记录的操作"为止

  14. 49 38 65 97 76 13 27 30 38 49 65 76 13 27 30 97 38 49 65 13 27 30 76 38 49 13 27 30 65 38 13 27 30 49 13 27 30 38 13 27 30 13 27 初始关键字 第一趟结果 第二趟结果 第三趟结果 第四趟结果 第五趟结果 第七趟结果 第六趟结果 冒泡排序示例之一: 38 13 49 27 38 13 38 30 49 27 13 49 30 27 65 38 76 13 49 27 76 30 65 13 97 30 76 65 97 27 76 97 30 97

  15. 49 38 65 97 76 13 27 30 38 49 65 76 13 27 30 97 初始关键字 第一趟 冒泡排序的算法: template <class Type> void BubbleSort (sortlist<Type> & table ) { int i = 1; while ( i < =table.CurrentSize-1){//表示要进行n-1趟 i++; }//while } for ( int j = 0; j <= CurrentSize-i-1; j++ ) if (table.Arr[j].GetKey() > table. Arr[j+1].GetKey()) ) Swap (table. Arr[j], table. Arr[j+1] ); 如果某一趟没有发生交换,说明什么问题? 说明已经是"正序",则可以提前结束。 怎么改进?

  16. 49 38 65 97 76 13 27 30 38 49 65 76 13 27 30 97 38 49 65 13 27 30 76 38 49 13 27 30 65 38 13 27 30 49 13 27 30 38 13 27 30 初始关键字 第一趟结果 第二趟结果 第三趟结果 第四趟结果 第五趟结果 第六趟结果 冒泡排序示例之二: 原来共需要七趟,但现在第六趟没有发生交换,则可提前结束

  17. 49 38 65 97 76 13 27 30 38 49 65 76 13 27 30 97 初始关键字 第一趟结果 冒泡排序的算法(改进): template <class Type> void BubbleSort (sortlist<Type> & table ) { int i = 1; while ( i < =table.CurrentSize-1 ){ for ( int j = 0; j < =table.CurrentSize-i+1; j++ ) if (table.Arr[j].GetKey() > table. Arr[j+1].GetKay() ) Swap (table. Arr[j], table. Arr[j+1] ); i++;}//while } int finish = 0;//0表示还没有排好序 && !finish finish = 1; //本趟开始时可认为已经正序,如果发生逆序,则改变finish值为0,表示还需要考察下一趟的情况 { finish = 0;} //排序结束标志置为0,表示本趟发生了交换,说明还不能结束,还需要考察下一趟的情况

  18. 冒泡排序示例之三: 第3趟没有发生交换!!

  19. 11 22 33 44 55 66 77 88 88 77 66 55 44 33 22 11 正序 逆序 • 算法评价(改进的冒泡排序) • 时间复杂度 • 最好情况(正序) • 比较次数:n-1(只要进行一趟即可) • 移动次数:0 • 最坏情况(逆序) • 比较次数:(需n-1趟,每趟达到最大比较次数) • 移动次数: 在最坏情况下,时间复杂度为:T(n)=O(n²) • 空间复杂度:S(n)=O(1) • 稳定性:冒泡排序算法是一种稳定的排序方法

  20. 9.2.2 快速排序 快速排序(Quick Sort)又被称做分区交换排序,这是一种平均性能非常好的排序方法。 其算法基本思想是:任取排序表中的某个数据元素(例如取第一个数据元素)作为基准,按照该数据元素的关键字大小,将整个排序表划分为左右两个子表: 左侧子表中所有数据元素的关键字都小于基准数据元素的关键字, 右侧子表中所有数据元素的关键字都大于或等于基准数据元素的关键字, 基准数据元素则排在这两个子表中间(这也是该数据元素最终应安放的位置), 然后分别对这两个子表重复施行上述方法的快速排序,直到所有的子表长度为1,则排序结束。

  21. 如何做到这一点 比如有结点的关键字序列F={k1,k2,…,kn},取k1为基准数据元素的关键字。则经过一次划分后就可以把序列F分成: k1’,k2’,…,ks’,k1,k1’’,k2’’,…,kt’’,其中:s+t+1=n 即:以k1为界把原序列F分成:F’,k1,F’’ 使得:F’中所有结点的关键字都小于k1, F’’中所有结点的关键字都大于等于k1 以上把序列F以k1为界分成两部分的过程叫"一趟"。 这时k1所在的位置也是该数据元素最终应安放的位置。

  22. ? 如何把关键字序列{k1,k2,…,kn }中的k1 放到它最终应安放的位置? 看看是否可以这样考虑: 首先把k1 x,然后依次考察ki(i=2,3,…,n) 若ki>x,则继续考察下一个结点ki+1 , 否则,把ki y; 然后把k1~ki-1这些结点依次往后移; 最后把y移到序列的第1个位置。 然后继续考察下一个结点ki+1 具体举例见黑板解释。 结论:如此处理不行!!!

  23. ? ? 如何把关键字序列{k1,k2,…,kn }中的k1 放到它最终应安放的位置? 可以这样考虑: 首先把k1 x,然后依次考察ki(i=2,3,…,n) 若ki>x,则继续考察下一个结点ki+1 , 否则,把ki y; 然后把k1~ki-1这些结点依次往后移; 最后把y移到序列的第1个位置。 然后继续考察下一个结点ki+1 具体举例见黑板解释。 结论:如此处理不行!!!

  24. 快速排序 • 排序过程:对r[s..t]中记录进行快速排序。 • 附设两个指针:左指针i和右指针j,设r[s]为枢轴元素。 即设:待排序记录为:r[s],r[s+1] ~,….,r[t] i j 初始时令i=s,j=t,且令x= r[s] (1)从j 所指位置向左(前)搜索,当r[j].key>=x.key时,r[j]不移动,修改j--,直到找到一个逆序的记录( r[j].key<x.key ),把r[j]移到左边i所指的位置,修改i,i++ (2)从r[i]向右(后)搜索,直到r[i].key>x.key,把r[i]移动到右边j所指的位置;然后修改右指针j,即j--。 重复上述两步(1)和(2),直至i==j为止,此处就是枢轴元素x所在的位置。 再分别对两个子序列进行快速排序,直到每个子序列只含有一个记录为止。

  25. 快速排序(第一趟数据移动情况) • 示例:设待排序记录的键值序列为{15,20,11,10,23,13} x x=15

  26. 快速排序过程示例1: 初始 [15 20 11 10 23 13] [13 10 11] 15 [23 20] 第一趟排序后 第二趟排序后 [11 10] 13 15 [23 20] 第三趟排序后 [10] 11 13 15 [23 20] 10 11 13 15 [20] 23 第四趟排序后,结束。

  27. 快速排序过程示例2:

  28. 快速排序算法的描述如下: template <class Type> void QuickSort ( sortlist<Type> &table, int low, int high ) { //在待排序区间lowhigh上,递归地进行快速排序 int i = low, j = high; element<Type> temp = table. Arr[low]; while ( i < j) { while ( i < j && temp.GetKey()<=table.Arr[j].GetKey() ) j--; if ( i < j ) { Swap(Table.Arr[i], Table.Arr[j] ); i++ ; } while ( i < j && temp .GetKey()>table.Arr[i].GetKey() ) i++; if ( i < j ) { Swap(Table.Arr[i], Table.Arr[j] ) ; j-- ; } } } while ( i < j) { }

  29. 快速排序算法的描述如下: template <class Type> void QuickSort ( sortlist<Type> &table, int low, int high ) { //在待排序区间lowhigh上,递归地进行快速排序 int i = low, j = high; element<Type> temp = table. Arr[low]; while ( i < j) { while ( i < j && temp.GetKey()<=table.Arr[j].GetKey() ) j--; if ( i < j ) { Swap(Table.Arr[i], Table.Arr[j] ); i++ ; } while ( i < j && temp .GetKey()>table.Arr[i].GetKey() ) i++; if ( i < j ) { Swap(Table.Arr[i], Table.Arr[j] ) ; j-- ; } } } while ( i < j) { } 这叫一趟 Table.Arr[i]= temp //将基准元素就位 QuickSort ( table, low, i-1); //在左子区间递归进行快速排序 QuickSort ( table, i+1, high ); //在右子区间递归进行快速排序

  30. 快速排序算法的描述如下: template <class Type> void QuickSort ( sortlist<Type> &table, int low, int high ) { //在待排序区间lowhigh上,递归地进行快速排序 int i = low, j = high; element<Type> temp = table. Arr[low]; while ( i < j) { while ( i < j && temp.GetKey()<=table.Arr[j].GetKey() ) j--; if ( i < j ) { Swap(Table.Arr[i], Table.Arr[j] ); i++ ; } while ( i < j && temp .GetKey()>table.Arr[i].GetKey() ) i++; if ( i < j ) { Swap(Table.Arr[i], Table.Arr[j] ) ; j-- ; } } Table.Arr[i]= temp //将基准元素就位 QuickSort ( list, low, i-1); //在左子区间递归进行快速排序 QuickSort ( list, i+1, high ); //在右子区间递归进行快速排序 } 算法还有问题吗? while ( i < j) { } 这叫一趟 • 如果关键字序列为:11,22,33,44,55,66,77,88,会怎么样? • 将会造成数组下标 j 越界!!! • 因此还要补充条件。 • 什么条件? • 要加上: i<j Table.Arr[i]= temp //将基准元素就位 QuickSort ( table, low, i-1); //在左子区间递归进行快速排序 QuickSort ( table, i+1, high ); //在右子区间递归进行快速排序

  31. 快速排序算法的描述如下: template <class Type> void QuickSort ( sortlist<Type> &table, int low, int high ) { //在待排序区间lowhigh上,递归地进行快速排序 int i = low, j = high; element<Type> temp = table. Arr[low]; while ( i < j) { while ( i < j && temp.GetKey()<=table.Arr[j].GetKey() ) j--; if ( i < j ) { Swap(Table.Arr[i], Table.Arr[j] ); i++ ; } while ( i < j && temp .GetKey()>table.Arr[i].GetKey() ) i++; if ( i < j ) { Swap(Table.Arr[i], Table.Arr[j] ) ; j-- ; } } Table.Arr[i]= temp //将基准元素就位 QuickSort ( list, low, i-1); //在左子区间递归进行快速排序 QuickSort ( list, i+1, high ); //在右子区间递归进行快速排序 } while ( i < j) { } 这叫一趟 i < j && if ( i < j ) { } i < j && if ( i < j ) { } Table.Arr[i]= temp //将基准元素就位 QuickSort ( table, low, i-1); //在左子区间递归进行快速排序 QuickSort ( table, i+1, high ); //在右子区间递归进行快速排序

  32. 例:已知关键字序列为: 11,22,33,44,55,66,77 请对其进行快速排序。 如此结果有点象什么样的排序方法? ? 初始: {11, 22,33,44,55,66,77} 第1趟 11,{22,33,44,55,66,77} 第2趟 11,22, { 33,44,55,66,77} 第3趟 11,22,33, { 44,55,66,77} 第4趟 11,22,33,44, { 55,66,77} 第5趟 11,22,33,44,55, { 66,77} 第6趟 11,22,33,44,55,66, { 77} 结束 结论:若初始记录序列按关键字有序或基本有序时,快速排序将蜕化为"冒泡排序"。

  33. 为此,在这种情况下,通常依"三者取中"的法则选取枢轴记录,即比较:为此,在这种情况下,通常依"三者取中"的法则选取枢轴记录,即比较: table. Arr[low].key, table. Arr[high].key, table. Arr[(low+high)/2].key, 取三者中其关键字为中间值的记录为枢轴,只要将该记录和table. Arr[low]互换,而以上算法不变。 low (low+high)/2 high

  34. 算法评价 • 时间复杂度 • 最好情况(每次总是选到中间值作枢轴)T(n)=O(nlog2n) • 最坏情况(每次总是选到最小或最大元素作枢轴)T(n)=O(n²) • 空间复杂度:需栈空间以实现递归 • 最坏情况:S(n)=O(n) • 一般情况:S(n)=O(log2n) • 稳定性: • 快速排序是一种不稳定的排序方法。

  35. 9.3 插入排序 插入排序的基本思想是: 每一次设法把一个数据元素插入到已经排序的部分序列的合适位置,使得插入后的序列仍然是有序的。 开始时建立一个初始的有序序列,它只包含一个数据元素。 然后,从这个的初始序列开始不断进行插入数据元素,直到最后一个数据元素插入到有序序列后,整个排序工作就完成了。

  36. 9.3.1 直接插入排序 已经有序 n 1 i-1 i 直接插入排序算法基本思想是: 开始时把第一个数据元素作为初始的有序序列, 然后依次从第二个数据元素开始把数据元素按关键字大小插入到已排序的部分排序表的适当位置。 当插入第i(1<i≤n)个数据元素时,前面的i-1个数据元素已经排好序,这时,用第i个数据元素的关键字与前面的i-1个数据元素的关键字顺序进行比较,找到插入位置后就将第i个数据元素插入,原来位置及其以后的数据元素都向后顺移一个位置。 如此进行n-1次插入就完成了排序。

  37. (1) 顺序表上的直接插入排序 排序结果: (13 27 38 49 65 76 97) j j j j j j 在顺序表上进行直接插入排序过程及每趟的结果: 下标 0 1 2 3 4 5 6 初始 ( ) 49 38 65 97 76 13 27 例 i=1 (38 49) 65 97 76 13 27 i=2 (38 49 65) 97 76 13 27 i=3 (38 49 65 97) 76 13 27 i=4 (38 49 65 76 97) 13 27 temp i=5 (13 38 49 65 76 97) 27 38 49 65 76 97 i=6 (13 38 49 65 76 97) 27 27

  38. 这叫一趟 i 第6趟结果 (13 27 38 49 65 76 97 ) 10 j j j j j j 直接插入排序算法的C++描述: template <class Type> void InsertionSort (sortlist<Type> & table ) { element<Type> temp ; int j ; for ( int i = 1; i < =table.CurrentSize-1; i++ ) { temp = table.Arr[i]; j = i-1 ; while ( temp.getKey ( ) < table.Arr[j].getKey ( ) ) { table.Arr[j+1] = table.Arr[j]; j--; } table.Arr[j+1] = temp; } } 下标 0 1 2 3 4 5 6 7 初始 ( ) 49 38 65 97 76 13 27 10 第5趟结果 (13 38 49 65 76 97) 27 10 temp 38 49 65 76 97 第6趟过程 (13 38 49 65 76 97) 27 10 27

  39. 这叫一趟 i 第6趟结果 (13 27 38 49 65 76 97 ) 10 j 直接插入排序算法的C++描述: template <class Type> void InsertionSort (sortlist<Type> & table ) { element<Type> temp ; int j ; for ( int i = 1; i < =table.CurrentSize-1; i++ ) { temp = table.Arr[i]; j = i-1 ; while ( temp.getKey ( ) < table.Arr[j].getKey ( ) ) { table.Arr[j+1] = table.Arr[j]; j--; } table.Arr[j+1] = temp; } } ? j > =0 && 下标 0 1 2 3 4 5 6 7 如果要插入最后一个元素10会怎么样? 初始 ( ) 49 38 65 97 76 13 27 10 第5趟结果 (13 38 49 65 76 97) 27 10 temp 38 49 65 76 97 第6趟过程 (13 38 49 65 76 97) 27 10 27 将会造成数组下标越界,使得j=-1

  40. 这叫一趟 直接插入排序算法的C++描述(采用监视哨技术): template <class Type> void InsertionSort (sortlist<Type> & table ) { int j ; for ( int i = i++ ) {//从1单元开始存 = table.Arr[i];j = i-1; while (getKey ( ) < table.Arr[j].getKey ( ) ) { table.Arr[j+1] = table.Arr[j]; j--; } table.Arr[j+1] = } } 2; i < =table.CurrentSize; table.Arr[0] table.Arr[0]. table.Arr[0]; 为了避免重复检测是否会数组越界,提高时间性能,还可以类似于顺序查找算法那样,采用"监视哨"技术加以改进。 template <class Type> void InsertionSort (sortlist<Type> & table ) {//改进前算法 element<Type> temp ;int j ; for ( int i = 1; i < =table.CurrentSize-1; i++ ) { temp = table.Arr[i]; j = i-1 ; while ( j>=0 && temp.getKey ( ) < table.Arr[j].getKey ( ) ) { table.Arr[j+1] = table.Arr[j]; j--; } table.Arr[j+1] = temp; } }

  41. 这叫一趟 i 第5趟结果 13 (13 38 49 65 76 97) j j j j j j 直接插入排序算法的C++描述(采用监视哨技术): template <class Type> void InsertionSort (sortlist<Type> & table ) { int j ; for ( int i = i++ ) { = table.Arr[i];j = i-1; while (getKey ( ) < table.Arr[j].getKey ( ) ) { table.Arr[j+1] = table.Arr[j]; j--; } table.Arr[j+1] = } } 2; i < =table.CurrentSize; table.Arr[0] table.Arr[0]. table.Arr[0]; 下标 0 1 2 3 4 5 6 7 初始 ( ) 49 38 65 97 76 13 27 第4趟结果 76 (38 49 65 76 97) 13 38 49 65 76 97 第5趟过程 13 (38 49 65 76 97) 13 13

  42. 算法评价 • 时间复杂度 • 若待排序记录按关键字从小到大排列(正序) • 关键字比较次数: • 记录移动次数:2(n-1) • 若待排序记录按关键字从大到小排列(逆序) • 关键字比较次数: • 记录移动次数: • 若待排序记录是随机的,取最好和最坏情况的平均值 • 关键字比较次数(约为): T(n)=O(n²) • 记录移动次数(约为) : • 空间复杂度:S(n)=O(1) • 稳定性:直接插入排序是一种稳定的排序方法

  43. (2) 链表上的直接插入排序 右图给出了在链表上实现直接插入排序的示例, 例子中用静态链表存储排序表,并把Arr[0]作为链表的表头结点。 初始时,排序表中的数据元素结点形成一个单链表。 在排序开始时,先把表头结点Arr[0]的next设为-1,表示有序链表为空表,p指向排序表的第一个数据元素结点。

  44. (2) 链表上的直接插入排序 下图给出了在链表上实现直接插入排序的示例。例子中用静态链表存储排序表,并把Arr[0]作为链表的表头结点。 初始时,排序表中的数据元素结点形成一个单链表。 在排序开始时,先把表头结点Arr[0]的next设为-1,表示有序链表为空表,p指向排序表的第一个数据元素结点。

  45. 静态链表表示的排序表的类定义: constint MaxSize = 100; template <class Type> class sortlinklist; template <class Type> class node { //数据元素的类定义 friendclass sortlinklist <Type>; private: Type key;//数据元素的关键字 intnext; //后继指针 other; //其它信息

  46. public: node( ) ; //构造函数 Type getKey ( ) { return key; } //取数据元素关键字 void setKey ( const Type k ) { key = k; } //修改数据元素关键字 int getLink ( ) { return next; } //取结点的后继指针 void setLink ( constint p ) { next = p; } //修改结点的后继指针 int operator == ( Type & k ) { return ! ( key < k || k < key ); } int operator != ( Type & k ) { return ( key < k || k < key );} int operator <= ( Type & k ) { return ! ( key > k ); } int operator >= ( Type & k ) { return ! ( key < k ); } int operator < ( Type & k ) { return ( key < k ); } int operator > ( Type & k ) { return ( key > k ); } }

  47. template <class Type> class sortlinklist { protected: node <Type> * Arr; //存储排序表结点的向量(静态链表) int CurrentSize; //排序表中的结点个数 public: sortlinklist ( ) : CurrentSize (0) { Arr = new node <Type> [MaxSize]; } //构造函数 sortlinklist ( ) { delete Arr[ ]}; //析构函数 }

  48. 在静态链表上实现直接插入排序的算法: 设辅助指针:p, q, pre , r; p—指向待排序表中的当前被考察的结点 q—指向已排序表的当前被比较的结点 pre—指向q的前驱结点 r—指向待排序表中当前被考察结点p的后继结点 算法思想可描述如下: [1]p指向第一个数据元素结点; [2]形成初始的已排序的空链表; [3]当待排序序列中还存在被考察的结点p时, [11] p取待排序表中下一个待考察结点,回到[3] 把p插入到已排序表的适当位置

  49. 在静态链表上实现直接插入排序的算法: 设辅助指针:p, q, pre , r; p—指向待排序表中的当前被考察的结点 q—指向已排序表的当前被比较的结点 pre—指向q的前驱结点 r—指向待排序表中当前被考察结点p的后继结点 算法思想可描述如下: [1]p指向第一个数据元素结点; [2]形成初始的已排序的空链表; [3]当待排序序列中还存在被考察的结点p时, [4] q指向已排序表的第一个元素; [5] pre指向q的前驱结点(可以是头结点); [6] 当被比较的已排序表还有结点q时,重复: [7] 如果p结点大于q结点,则继续比较后面的q; [8] 否则,说明已经找插入位置,则跳出循环 [9] r指向当前被考察的待插入结点p的后继结点 [10] 在pre与q之间插入p,使p成为pre的后继,q的前驱 [11] p取待排序表中下一个待考察结点,即p指向r所指结点,回到[3]

  50. 在静态链表上实现直接插入排序的算法: 设辅助指针:p, q, pre , r; p—指向待排序表中的当前被考察的结点 q—指向已排序表的当前被比较的结点 pre—指向q的前驱结点 r—指向待排序表中当前被考察结点p的后继结点 算法思想可描述如下: [1]p指向第一个数据元素结点; [2]形成初始的已排序的空链表; [3]当待排序序列中还存在被考察的结点p时, [4] q指向已排序表的第一个元素; [5] pre指向q的前驱结点(可以是头结点); [6] 当被比较的已排序表还有结点q时,重复: [7] 如果p结点大于q结点,则继续比较后面的q; [8] 否则,说明已经找插入位置,则跳出循环 [9] r指向当前被考察的待插入结点p的后继结点 [10] 在pre与q之间插入p,使p成为pre的后继,q的前驱 [11] p取待排序表中下一个待考察结点,即p指向r所指结点,回到[3]

More Related