希尔排序(Shell Sort)
我们注意到:在插入排序中,列表越接近有序,比对次数就越少。从这个情况入手,希尔排序以插入排序作为基础,对无序表进行**“等间隔”划分子列表**,每个子列表都执行插入排序。
比如,间隔为3进行分组,如下图,每行深色的为一组:
间隔为多少,那么子列表就有多少个。随着间隔的缩小,子列表的数量越来越少,无序表的整体越来越接近有序,从而减少整体排序的比对次数。
对3个子列表分别进行插入排序后,整个表的状态(最下面一行):
最后一趟是标准的插入排序,但由于前面几趟已经将列表处理到接近有序,这一趟仅需少数几次移动即可完成。
python代码实现
子列表的间隔一般从n/2开始,每趟倍增:n/4, n/8……直到1。
def shell_sort(a_list):
# 初始化间隔大小
sublist_count = len(a_list) // 2
# 只要间隔大于0,就对子列表进行排序
while sublist_count > 0:
for start_position in range(sublist_count):
gap_insertion_sort(a_list, start_position, sublist_count)
# 缩小间隔
sublist_count = sublist_count // 2
def gap_insertion_sort(a_list, start, gap):
"""
带间隔的插入排序
:param a_list: 需要排序的列表
:param start: 偏移量
:param gap: 间隔的大小
"""
# 从每个子列表的第start个元素开始,进行带间隔的插入排序
for i in range(start + gap, len(a_list), gap):
current_value = a_list[i]
position = i
# 当同一组中有逆序的数据项时,按照间隔移动数据项,腾出空位
while position >= gap and a_list[position - gap] > current_value:
a_list[position] = a_list[position - gap]
# 空位的位置向前移gap位
position = position - gap
# 将当前数据项放入空位
a_list[position] = current_value
算法分析
粗看上去,希尔排序以插入排序为基础,可能并不会比插入排序好。但由于每趟都使得列表更加接近有序,这过程会减少很多原先需要的“无效”比对。
希尔排序的详尽分析比较复杂,大致说是介于 O ( n ) O(n) O(n)和 O ( n 2 ) O(n^2) O(n2)之间。如果将间隔保持在 2 k − 1 2^k-1 2k−1(1、3、5、7、15、31等等),希尔排序的时间复杂度约为 O ( n 3 2 ) O(n^{ \frac {3}{2}}) O(n23)。