概述
排序算法的分类
- 插入类排序
直接插入排序、折半插入排序、希尔排序
- 交换类排序
冒泡排序、快速排序
- 选择类排序
简单选择排序、堆排序
- 归并类排序
二路归并排序
- 基数类排序
基数类排序基于多关键字排序的思想,把一个逻辑关键字拆分成多个关键字。
插入类排序
直接插入排序
- 算法思想
每趟将一个待排序的关键字按照其值的大小插入到一句排好的部分有序序列的适当位置上,直到所有待排关键字都被插入到有序序列为止。
- 代码
def InsertSort(arr):
n = len(arr)
for i in range(n):
temp = arr[i]
j = i - 1
while j >= 0 and temp < arr[j]:
arr[j+1] = arr[j]
j = j - 1
arr[j+1] = temp
- 算法性能分析
3.1 时间复杂度分析
由插入排序算法代码,选取最内层循环中的arr[j+1] = arr[j],这一句作为基本操作。
考虑最坏情况,即整个序列是逆序的,则内层循环中temp < arr[j] 这个条件是始终成立的。此时对于每次外层循环,最内层循环的执行次数达到最大值,为i次。i取值为1到n-1,由此可得基本操作总的执行次数为n(n-1)/2,可以看出时间复杂度为\(O(n^2)\)。
考虑最好情况,即整个序列已经有序,则对于内层循环中temp < arr[j] 这个条件始终不成立,此时内层循环始终不执行,双层循环就成了单层循环,循环内操作都是常量级,时间复杂度为O(n)。
综上,算法平均时间复杂度为\(O(n^2)\)。
3.2 空间复杂度分析
算法所需辅助存储空间不随待排序列规模的变化而变化,是个常量,因此空间复杂度O(1)。
折半插入排序
- 算法思想
折半插入排序的基本思想和直接插入排序类似,区别是查找插入位置的方法不同,折半插入排序是采用折半查找法来查找插入位置的。
折半查找法的一个基本条件是序列已经有序,而从直接插入排序的流程中可以看出,每次都是在一个已经有序的序列中插入一个新的关键字,因此可以用折半查找法在这个有序序列中查找插入位置。
- 性能分析
2.1 时间复杂度分析
折半插入排序适合关键字数较多的场景,与直接插入排序相比,折半插入排序在查找插入位置上面所花时间大大减少。折半插入排序在关键字移动次数方面和直接插入排序是一样的,所以时间复杂度和直接插入排序还是一样的。
折半插入排序的关键字比较次数和初始序列无关。因为每趟排序折半查找插入位置时,折半次数是一定的(都是在low>high时结束),折半一次就要比较一次,所以比较次数是一定的。
由此可知折半插入排序的时间复杂度最好情况为\(O(nlog_2 n)\),最差情况为\(O(n^2)\),平均情况为\(O(n^2)\)。
2.2 空间复杂度分析
空间复杂度和直接插入排序一样,为O(1)。
希尔排序
- 算法介绍
希尔排序又叫做缩小增量排序,本质还是插入排序,只不过是将待排序列按某种规则分成几个子序列,分别对这几个子序列进行直接插入排序。这个规则的体现就是增量的选取,如果增量为1,就是直接插入排序。例如,先以增量5米分个序列,即将下标为0、5、10、15...的关键字分成一组,将下标为1、6、11、16...的关键字分成另一组等,然后分别对这些组进行直接插入排序,这就是一趟希尔排序。将上面排好序的整个序列,再以增量2分割,即将下标为0、2、4、6、8...的关键字分成一组,将下标为1、3、5、7、9...的关键字分成另一组等,然后分别对这些组进行直接插入排序,这又完成了一趟希尔排序。最后以增量1分割整个序列,其实就是对整个序列进行一趟直接插入排序,从而完成整个希尔排序。
注意到增量5、2、1是逐渐缩小的,这就是缩小增量排序的由来。前面讲过,直接插入排序适合于序列基本有序的情况,希尔排序的每趟排序都会使整个序列变得更加有序,等整个序列基本有序了,再来一趟直接插入排序,这样会使排序效率更高,这就是希尔排序的思想。
- 性能分析
2.1 时间复杂度
希尔排序的时间复杂度和增量选取有关,希尔排序的增量选取规则有很多,常见的增量选取规则有以下两个。
2.1.1 希尔自己提出的:
\(\lfloor n/2 \rfloor、\lfloor n/4 \rfloor、...、\lfloor n/2^k \rfloor、...、2、1\)
即每次将增量除以2并向下取整,其中n为序列长度,此时时间复杂度为\(O(n^2)\)。
2.1.2 帕佩尔诺夫和斯塔舍维奇(Papernov & Stasevich)提出的: