第8章学习小结
第8章学习了排序(主要是内部排序),可以分为插入排序、交换排序、选择排序、归并排序。内部排序的过程是一个逐步扩大记录的有序序列长度的过程。
排序算法效率的评价指标有两个方面:执行时间和辅助空间
(1)执行时间:比较次数和移动次数都应该尽可能少
(2)辅助空间:理想的空间复杂度为O(1),即算法执行期间所需要的辅助空间与待排序的数据量无关。
一、插入排序
直接插入排序(基于顺序查找)
课本P237页 例8.1 手工实现直接插入排序
void InsertSort(SqList &L) {//对顺序表L做直接插入排序 for(int i=2 ; i<L.length ; ++i) { if(L.r[i].key < L.r[i-1].key) //小于,需将r[i]插入有序子表 { L.r[0] = L.r[i] ; //将待插入的记录暂存到监视哨中 L.r[i] = L.r[i-1] ; //r[i-1]后移 for(int j=i-2 ; L.r[0].key < L.r[j].key ; --j) //从后往前寻找插入位置 { L.r[j+1] = L.r[j] ; //记录逐个后移,直到找到插入位置 } L.r[j+1] = L.r[0] ; //将r[0]即原r[i],插入到正确位置 } }
(1)时间复杂度:O(n*n)
(2)空间复杂度:O(1)
(3)算法特点:稳定排序,算法简便,更适合于除湿机路基本有序(非降序)的情况。
折半插入排序(基于折半查找)
在插入第i个记录时,需要经过log2n次比较,才能确定它应插入的位置
需要循环n-1次,每次先使用折半查找法,查找r[i]的插入位置,然后将r[i]插入表长为i-1的有序序列中,直到将r[n]插入表长为n-1的有序序列为止。
(1)时间复杂度:O(n*n)
(2)空间复杂度:O(1) (和直接插入排序相同,辅助空间只需要r[0])
(3)算法特点:稳定排序,只能用于顺序结构,适合初始记录无序、n较大的情况。
希尔排序(缩小增量排序)
课本P240 例8.2 手工实现排序
希尔排序:第一趟取增量d1(d1<n)把全部记录分成d1个组,所有间隔为d1的记录分在同一组,在各个组中进行直接插入排序
第二趟取增量d2(d2<d1),重复上述的分组和排序
依次类推,直到所取的增量dt = 1,所有记录在同一组中进行直接插入排序为止。
(1)时间复杂度:O(n3/2)
(2)空间复杂度:O(1)(和前面一样,只需要一个辅助空间r[0])
二、交换排序
冒泡排序
课本P242 例8.3 手工实现排序
冒泡排序:在每一趟中,将每个记录的关键字与下一个记录相比较,如果L.r[i].key > L.r[i+1].key,则交换两个记录。依次类推,直到比较完第n-1个记录和第n个记录为止,这样就完成了第一趟冒泡排序。此时关键字最大的记录处于正确的位置(第n个)。
然后进行第二趟冒泡排序,对前n-1个记录进行同样的操作,结果是关键字次大的记录处于第n-1个位置上。
重复这样的操作,直到在某一趟排序过程中没有进行过交换记录的操作,说明序列已全部达到排序要求。
(1)时间复杂度:O(n*n)
(2)空间复杂度:O(1)(在交换时只需要一个辅助空间用来暂存记录)
(3)算法特点:稳定排序,当初始记录无序、n较大时,不宜采用冒泡排序
快速排序
课本P244 例8.4 手工实现排序
快速排序采用 “分治法” 策略把一个序列分为较小和较大的两个子序列,然后递归地排序两个子序列。
快速排序:预处理:R(low).key,R(high).key 和 R((low+high)/2).key三者取中
挑选基准值:从数列中挑出一个元素,称作 “基准”
分割:重新排序序列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成。
递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。
(1)时间复杂度:O(nlog2n)
(2)空间复杂度:最好情况下 O(log2n),最坏情况下 O(n)
(3)算法特点:不稳定排序,适合于顺序结构,适用于初始记录无序、n较大的情况
三、选择排序
简单选择排序
课本P247 例8.5 手工实现排序
简单选择排序:第一趟从r[1]开始,通过n-1次比较,从n个记录中选出关键字最小的记录,记为r[k],交换r[1]和r[k]
第二趟从r[2]开始,通过n-2次比较,从n-1个记录中选出关键字最小的记录,记为r[k],交换r[2]和r[k]
依次类推...经过n-1趟,排序完成
void SelectSort(SqList &L) { for(int i=1 ; i<L.length ; ++i) { k = i ; for(int j=i+1 ; j<=L.length ; ++j) {//选择关键字最小的记录 if(L.r[j].key < L.r[k].key) { k = j ; //k指向此趟排序中关键字最小的记录 } } if(k != i) {//交换r[i]与r[k] t = L.r[i] ; L.r[i] = L.r[k] ; L.r[k] = t ; } } }
(1)时间复杂度:O(n*n)
(2)空间复杂度:O(1)(同冒泡排序一样,只有在交换两个记录时需要一个辅助空间)
(3)算法特点:不稳定排序
堆排序
将序列看成是一个完全二叉树,树中所有非终端结点的值均不大于(不小于)其左、右孩子结点的值。
大根堆(小根堆):堆顶记录的关键字最大(最小)(如果求非降序,需要建立大根堆)
课本P250、253
(1)时间复杂度:O(nlog2n)
(2)空间复杂度:O(1)(仅需要一个记录大小供交换用的辅助存储空间)
(3)算法特点:不稳定排序
四、归并排序
归并排序
课本P254 例8.8 手工实现排序
归并排序:设初始序列含有n个记录,则可看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到 n/2 个长度为2或1的有序子序列;再两两归并,重复操作,直到得到一个长度为n的有序序列为止。
(1)时间复杂度:O(nlog2n)
(2)空间复杂度:O(n)(用顺序表实现归并排序时,需要和待排序记录个数相等的辅助存储空间)
(3)算法特点:稳定排序