插入排序每次只能将数据移动一位。
已知插入排序代码为:
def insert_sort(a):
for i in range(1,len(a)):
j=i-1
while j>=0 and a[j]>a[i]:
a[j+1]=a[j]
j-=1
a[j+1]=a[i]
return a
希尔排序在插入排序的基础上,将数据移动n/2,n/4,…,1位。
for i in range(gap, n):
temp = a[i]
:把a[i]的值赋给temp,用于比较
j = i
j指针从i开始,表示的是从后往前遍历比大小的下标。
当j >= gap(为了确保后面的j-gap下标还在数组索引里,不会超出范围) and a[j - gap] > temp(j-temp对应的数大于j对应的数,把j-gap的数放到原来的j的位置,然后再往前对比)
:
此时j下标对应的数是原来j-gap对应的数:
a[j] = a[j - gap]
因为根据插入排序原理,把要插入的数的大小temp跟前面已经排序好的数以步长为1( j -= 1
)从后往前进行对比,希尔排序也是把要插入的数的大小temp跟前面已经排序好的数从后往前对比,但是是以步长为gap往前遍历。所以: j -= gap
跳出while
循环的条件是已经把前面gap步长遇到的所有值排好序了,而且原来的a[j]=a[j-gap]
是把j-gap比temp大的值放到了j的位置,那原来的j-gap的位置就空了出来,j-=gap
操作后,原来的j-gap就是新的j的位置,把temp放到这个位置:a[j] = temp
。实现了如果a[j - gap] > temp
(a[i]),那么交换位置的操作。
用gap //= 2
不断缩小步长,直到gap=1跟插入排序一致,此时所有数据顺序都排好了。
比如有一组示例数据如下
7 | 2 | 9 | 1 | 5 | 4 | 6 |
---|
a
数组长度是7
这里的gap初始值是7//2=3
a[i]初始值从3开始,a[3]=1。
接下来就是以gap的步长往前遍历,把数组分为4组:
a[j-temp] temp
7 1
2 6
9 4
1 6
然后进行对比,如果a[j - gap] > temp
,交换位置。
这样处理后的数组为:
1 | 2 | 4 | 7 | 5 | 9 | 6 |
---|
gap //= 2
,gap=1。
a[i]初始值从1开始,a[1]=2。
接下来就是以gap的步长往前遍历比大小:
a[j-temp] temp
1 2
# 1247596
--------------
2 4
1 4
# 1247596
--------------
4 7
2 7
1 7
# 1247596
--------------
7 5
# 5,7互换
4 5
2 5
1 5
# 1245796
--------------
7 9
5 9
4 9
2 9
1 9
# 1245796
--------------
9 6
# 6,9互换
7 6
# 6,7互换
5 6
4 6
2 6
1 6
# 1245679
然后进行对比,如果a[j - gap] > temp
,交换位置。
这样处理后的数组为:
1 | 2 | 4 | 5 | 6 | 7 | 9 |
---|
def shell_sort(a):
n = len(a)
gap = n // 2
while gap > 0:
for i in range(gap, n):
temp = a[i]
j = i
while j >= gap and a[j - gap] > temp:
a[j] = a[j - gap]
j -= gap
a[j] = temp
gap //= 2
希尔排序(Shell Sort)比插入排序(Insertion Sort)更高效的原因是因为希尔排序通过使用间隔序列在排序过程中引入了数据交换的“跳跃”。这种跳跃允许算法在内部循环中进行更远距离的交换,从而减少了元素比较和移动的次数。
对比代码如下:
import random
import time
def insertion_sort(arr):
comparisons = 0
moves = 0
for i in range(1, len(arr)):
key = arr[i]
j = i - 1
while j >= 0 and key < arr[j]:
comparisons += 1
arr[j + 1] = arr[j]
moves += 1
j -= 1
comparisons += 1
arr[j + 1] = key
return comparisons, moves
def shell_sort(arr):
comparisons = 0
moves = 0
gap = len(arr) // 2
while gap > 0:
for i in range(gap, len(arr)):
temp = arr[i]
j = i
while j >= gap and arr[j - gap] > temp:
comparisons += 1
arr[j] = arr[j - gap]
moves += 1
j -= gap
comparisons += j >= gap
arr[j] = temp
gap //= 2
return comparisons, moves
# 生成随机数组
array_size = 1000
random_array = [random.randint(1, 10000) for _ in range(array_size)]
# 复制数组以保持原始数组不变
insertion_array = random_array.copy()
shell_array = random_array.copy()
# 插入排序
start_time = time.time()
insertion_comparisons, insertion_moves = insertion_sort(insertion_array)
insertion_time = time.time() - start_time
# 希尔排序
start_time = time.time()
shell_comparisons, shell_moves = shell_sort(shell_array)
shell_time = time.time() - start_time
print(f"Insertion Sort: Comparisons={insertion_comparisons}, Moves={insertion_moves}, Time={insertion_time:.6f}s")
print(f"Shell Sort: Comparisons={shell_comparisons}, Moves={shell_moves}, Time={shell_time:.6f}s")
Insertion Sort: Comparisons=245538, Moves=244539, Time=0.035560s
Shell Sort: Comparisons=14866, Moves=7356, Time=0.000997s