排序2 - 希尔排序
希尔排序(shell sort)是改进后的插入排序,不同的是其设置一个步长,将输入按步长分组,对每组元素进行插入排序。然后减少步长,再次分组插入排序。直到步长变为1,也即对所有元素进行插入排序。
说明
一般来说,习惯于将初始步长设置为 n/2, 然后每次减半,直至为1。一个步长对应一趟分组排序。
找到一个特别好的图说明一下,出处为https://www.cnblogs.com/chengxiao/p/6104371.html。
实现
鄙人不才,用python实现了一个版本,代码如下:
def shell_sort(A): if(len(A)<=1): # 处理边界情况 return gap = len(A)/2 # 初始化步长 while(gap>0): for i in xrange(0, gap): # 进行gap次排序 start = i + gap while(start<len(A)): # 对一组元素进行插入排序 flag = A[start] insert_index = start while(insert_index-gap>=0 and A[insert_index-gap]>flag): A[insert_index] = A[insert_index-gap] insert_index -= gap A[insert_index] = flag start += gap gap /= 2 # 减少步长
问题
初学者可能就有很多疑问:
- 为什么要分组排序,还要改变步长多次操作,一次搞定不是更好吗?
- 分组排序方法,为什么要是插入排序,为什么不是快速排序,冒泡排序之类的办法?
回答第一个问题,虽然插入排序的次数变多了,但时间复杂度降低了。正常情况时间复杂度为O(n1~2),即使在现在,数学上也很难证明希尔排序的时间复杂度。有人用大量实验数据作统计,在排序数组足够大的情况下,时间复杂度约为O(n1.3)。直观上可以这么理解,每次分组排序使得组内的数字有序,进而整体的有序性增加。使得下次减少步长再作排序所花费的时间变少。最终这个累加使得整体的时间复杂度降低。
第二个问题,其实这个问题我也没有搞清楚,毕竟希尔排序的时间复杂度尚且无法求解,换句话说还无法给出严谨的数学解释。换个排序办法行不行?肯定行,但时间复杂度至少没有插入排序的好。冒泡排序同选择排序,稳定在O(n2)的时间复杂度,先否定。至于为什么不是其他排序方法,如果有大神看到这个问题,求解释!
复杂度和稳定性
时间复杂度O(n1~2);
未用到额外空间,空间复杂度O(n);
毫无疑问,分组排序的过程中,组内排序使得组之间元素的相对位置发生了改变,为不稳定排序算法。