希尔排序思想
有点类似归并可以分为两步:
(1)拆分; (2) 排序。并且不断重复这两个步骤。
- 拆分:gap将数据拆成一个个小的逻辑小组,并且gap不断减少。一开始每个组最多只有2个元素,最后慢慢变成整体数据的一半,然后变成整体。而gap从一开始的int(len/2)最后变为2.
- 排序:对每个小组进行插入排序。
思路形象记忆:
其实就升级版插入排序,插入排序适用于小规模或基本有序,希尔排序融入分治的思想,用一个个小的插入排序去解决大规模、无序的问题。
代码实现要注意的地方
- 考虑极端情况;
- gap要记得int一下;
- 只要j小于gap,说明还可以向左走,所以
j>=gap
; - 只要
arr[j-gap]>tmp
,说明需要向左走; - 不是排完一个逻辑分组才去排另外一个,而是同时的。每一组每组都优化一个数;
- 特点:其实就是在插入排序上多一个while循环判断gap。三个循环都与gap有关。
while gap>0
判断gap是否需要继续减半。
for i in range(gap, arr_len)
相当于选中插入排序时,准备插队的那个未排序数,而且这个i的特点是,轮流指向不同的逻辑分组,例如假设长度为8,gap=2时,此时有两个逻辑分组,i会分别取2,3,4,5,6,7。当i=2,4,6时,其实是对第一个逻辑分组进行插入排序(位置0即为第一个逻辑分组中默认已排序的初始值);当i=3,5,7时,其实是对第一个逻辑分组进行插入排序(位置1即为第二个逻辑分组中默认已排序的初始值)。
while j>=gap and arr[j-gap]>tmp
就是插入排序。j的判断值其实就是下面arr[j] = arr[j-gap]
中后面的变量arr[j-gap]
索引大于等于0的条件,即j-gap>=0
,故j>=gap
。(在插入排序中为j>=0
,因为插入排序中赋值的时候是arr[j+1] = arr[j]
,其实本质是一样的。插入排序代码)
代码
def shell_sort(arr):
# 排序初始值
arr_len = len(arr)
gap = int(arr_len/2)
# 特殊情况考虑
if arr_len==0:
return None
if arr_len==1:
return arr
# 只要增量未减为0,则继续
while gap>0:
for i in range(gap, arr_len):
tmp = arr[i]
j=i
# 单组插入排序
# 只要j小于gap,说明还可以向左走
# 只要arr[j-gap]>tmp,说明需要向左走
while j>=gap and arr[j-gap]>tmp:
# 比tmp大的值右移
arr[j] = arr[j-gap]
# j减少增量值
j -= gap
# 若找到最佳位置,则插入
# 因为一旦j大于gap则说明已经是最左边的数了,所以最佳位置就是j
arr[j] = tmp
# 每个逻辑分组都排序后,gap减半
gap = int(gap/2)
return arr
arr = [3,2,1,5,6,5]
shell_sort(arr)
print(arr)
参考:希尔排序–简单易懂图解