算法思想:
将序列以某一增量分割为若干个子序列,对每个子序列进行插入排序,之后对增量进行递减,重复上述步骤,直到增量为1时,此时进行的插入排序就是基本的插入排序,完成后序列有序。
这里的增量的取值不固定,每一种取值方法造成的时间复杂度不一样。
我们一般以序列长度的一半为增量起始值。即 n//2作为增量起始值。
可以发现,每次插入排序,会把每次增量分割得到的子序列进行排序。可以快速使序列向有序方向发展。最后一次增量为1,就是执行一次常规的插入排序,遍历整个序列完成排序。
因为当增量为1时,序列已基本有序,交换次数大大减小,规避了从序列末尾一直交换到序列首部等耗时操作。因此希尔排序也可以看做插入排序的一种更高效的改进版本。
实现代码:
将插入排序理解为打扑克牌时揭牌,插入到手中的牌,插入时从右边较大的牌开始比较,遇到比左边大的就插在它右边,这样过程会很简单。
# 将插入排序进行修改
def insert_sort_gap(li, gap): # gap 为间隔
for i in range(gap, len(li)): # i 表示摸到牌的下标
tmp = li[i] # 将数暂存到tmp
j = i - gap # j指得是手里的牌的下标,起始为最右边的下标
while j >= 0 and li[j] > tmp: # 让tmp与前面的牌比较大小
li[j + gap] = li[j] # tmp小于前面的牌就与它交换位置
j -= gap
li[j + gap] = tmp # 当tmp大于了前面的牌时或前面没有牌了,就把tmp插入到其后面
def ShellSort(li):
d = len(li) // 2 # 增量,我们定为序列长度的一半
while d >= 1: # 当增量为1时进行排序就是执行的基本的插入排序,完成后序列有序
insert_sort_gap(li, d) # 对不同增量进行插入排序,使序列逐渐有序
d //= 2 # 每次增量插入排序完成后,增量进行递减,直到为1最后一次进行插入排序。
li = [5, 2, 8, 9, 6, 4, 7, 1, 3]
ShellSort(li)
print(li) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# -----------------------------下面是整合到一起的代码------------------------------
def ShellSort(li):
d = len(li) // 2 # 增量,我们定为序列长度的一半
while d >= 1: # 当增量为1时进行排序就是执行的基本的插入排序,完成后序列有序
for i in range(d, len(li)): # i 表示摸到牌的下标,数的下标
tmp = li[i] # 将数暂存到tmp
j = i - d # j指得是手里的牌的下标,起始为最右边的下标
while j >= 0 and li[j] > tmp: # 让tmp与前面的牌比较大小
li[j + d] = li[j] # tmp小于前面的牌就与它交换位置
j -= d
li[j + d] = tmp # 当tmp大于了前面的牌时或前面没有牌了,就把tmp插入到其后面
d //= 2 # 每次增量插入排序完成后,增量进行递减,直到为1最后一次进行插入排序。
li = [5, 2, 8, 9, 6, 4, 7, 1, 3]
ShellSort(li)
print(li) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
排序过程如图所示: