[Alg]排序算法之选择排序
作者:屎壳郎 miaosg01@163.com
时间:Aug 2021
版次:初版
简介: 当数据量较大时,堆排序优于shell排序。但无论如何比不过快速排序。快排的效率只是平均意义上的(概率上说),最坏的情况会退化至 N 2 N^2 N2。堆排序的优势在于,在恶化的情况下也能保证时间复杂度在 O ( N lg N ) O(N\lg N) O(NlgN)。后续我们会讲到归并排序也有这个特性,任何情况下都能保证 N lg N N\lg N NlgN,但归并排序要求更多的空间。
1、直接选择排序
直接选择排序遍历所有记录N,确定最大值(或最小值),然后输出到最终位置。缩小范围至剩余N-1,重复这个步骤。这个过程和直接插入排序完全相反,直接插入排序是从1个记录开始,一个接一个的往里插入记录,并逐步扩大遍历范围。选择排序直接确定记录的最终位置,而插入排序直到插入最后一个记录后在能确定记录的最终位置。
直接选择排序很简单,直接列算法,一眼就看明白,不须解释。
算法S:(直接选择排序)
设记录 R 1 , R 2 , … , R N R_1,R_2,\ldots,R_N R1,R2,…,RN,其键值 K 1 , K 1 , … , K N K_1,K_1,\ldots,K_N K1,K1,…,KN。
- S1.[循环j] For j = N , N − 1 , … , 2 j=N, N-1,\ldots,2 j=N,N−1,…,2,执行S2和S3.
- S2.[查找最大值 m a x ( K 1 , K 2 , … , K j ) max(K_1,K_2,\ldots,K_j) max(K1,K2,…,Kj)] 遍历 K j , K j − 1 , … , K 1 K_j,K_{j-1},\ldots,K_1 Kj,Kj−1,…,K1,确定最大值 K i K_i Ki.
- S3.[交换] 交换 R i ↔ R j R_i\leftrightarrow R_j Ri↔Rj,此时 R j , … , R N R_j,\ldots,R_N Rj,…,RN为最终位置。
要从N个记录中确定最大值,至少比较N-1次,所以直接选择排序的时间复杂度 O ( N 2 ) O(N^2) O(N2)。比较次数是没办法节省的,难道就没有改进的余地了吗?要改进算法的效率,无外乎两种途径,其一是对要待解决问题的本质有深入的洞察,并有深厚的数学功底来确立模型,建立高效算法。这点好像比较难。另一个相对简单的方法,不要每次都从零开始,要善于并充分利用前序的工作成果来减小后续的工作量或改善后续工作的复杂度。
对于第一种改进算法的方式,我们直接跳过就好了,毕竟那是计算机专家兼数学家或数学家兼计算机专家的饭碗,做人不能做绝了,给他们留口饭吃。我们重点来讲后一种方法。
我们在遍历 R j … R 1 R_j\ldots R_1 Rj…R1过程中,如果没有第一次就选中最大值,遍历过程会发生替换,产生一个序列。例如(1 3 2 5 4)第一次选 m a x = 4 max=4 max=4,继续向左搜索,找到 5 > 4 5 > 4 5>4,替换 m a x = 5 max=5 max=5,确定5为最大值。直接选择排序没有充分利用这个序列,直接舍弃4(第二大值),下次又从头开始。虽然第一次遍历不能减少比较次数,但我们可以充分利用遍历过程产生的有价值信息。
直接选择排序改进1
对算法做如下改进:
我们需要一个辅助表AUX记录当前最大值信息。我们改为从左到右遍历,这样更方便一点,从max=1开始,遇到3令max=3并保存1的位置,继续向右搜索遇到5,令max=5,并保存3的位置信息2。当5和4交换后,不必从头开始查找,取出辅助表中的位置信息2,从此处开始,位置2代表的意义为,K[2]是从头到2位置的最大值,不必查找位置2以前的记录。这就充分利用了以前比较的成果,减少了不必要的比较。采取这项改进措施,能把平均比较次数减半。
2、方根选择
另一个利用前序比较结果的例子是分组,这种方法不需要额外的空间存储位置信息。下面介绍称之为方根选择的分组选择方法。设 N N N 项记录,分为 N \sqrt N N 组,每组 N \sqrt N N