十大排序、七大查找算法python实现——希尔排序(shell sort)
原理参考链接:https://www.cnblogs.com/onepixel/articles/7674659.html
希尔排序的原理是,通过设定步长序列,将无序数组划分成不同的子数组,然后分别对子数组进行插入排序,随着步长的不断缩小,数组也逐渐变成有序。具体步骤如下:
- 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
- 按增量序列个数k,对序列进行k 趟排序;
- 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
def shell_sort(L):
'''希尔排序,升序'''
n = len(L)
print(L)
if n <= 1:
return
gap = n // 2 # 初始步长
while gap > 0: # 最后一次步长为1(即普通的插入排序),然后整个希尔排序结束
# 想像成,以步长 gap 将原始序列划分成 gap 个待排序的序列,对每个序列使用普通的插入排序进行排序
# 序列1: [L[0], L[gap], L[2*gap]...]
# 序列2: [L[1], L[1+gap], L[1 + 2*gap]...]
# 序列3: [L[2], L[2+gap], L[2 + 2*gap]...]
# 请查看插入排序算法 https://github.com/wangy8961/python3-algorithms/blob/master/4.%20%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%20-%20Insertion%20Sort/3_best_insertion_sort_asc.py
# 助记:普通的插入排序算法中步长是 1 ,把插入排序中的步长 1 替换为 gap
for i in range(gap, n): # "未排序序列" 的第1个元素分别是L[gap], L[1+gap], L[2+gap] ... ,所以变量 i 表示的下标是 gap, 1+gap, 2+gap ...
temp = L[i]
j = i
# j >= gap是因为后续j[j-gap],否则下标越界
while j >= gap and temp < L[j-gap]:
L[j] = L[j-gap]
j -= gap
L[j] = temp
print(L)
gap = gap // 2 # 得到新的步长
如何理解上述代码呢?
首先,我们需要设置步长序列,通过条件gap>0的while循环,不断让gap整除2,以得到{n/2,n/4,n/8,n/16,...1}的步长序列,对于每个步长序列,我们可以得到数量分别为n/2,n/4,...,1的子数组;
然后,使用直接插入排序的方法对这些子数组进行排序,如下述代码所示,这里就是对子数组进行插入排序,看过我的插入排序的小伙伴们一定知道,在这里不同的地方就在于将L[j-1]换成了L[j-gap],这是为什么呢?其实,我们可以将原数组划分成[L[0], L[1],L[2],...,L[gap-1], L[gap],L[gap+1],...,L[2*gap]],其中,如果这是执行第一遍的时候,L[2*gap]即是L[n],现在就显而易见了吧,在插入排序算法中,我们将第一个元素作为有序数组,从第二个元素开始进行与有序数组比较,而在子数组中j在区间[gap,n]中,也就是说L[j]即为第二个元素,L[j-gap]即为第一个元素,到此我们是不是很清楚的理解了希尔排序的执行过程了。
while j >= gap and temp < L[j-gap]:
L[j] = L[j-gap]
j -= gap
L[j] = temp
算法执行结果如下: