社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
插入排序每步将一个带排序的对象,按其关键码大小,插入到前面已经排好序的一组对象的适当位置上,知道对象全部插入为止。插入排序边插入边排序,子序列随时都是排好序的。
插入排序一般分三步进行:
(1)在R[1…i-1]中查找R[i]的插入位置; R[1..j].key < R[i].key< R[j+1..i-1].key
(2)将R[j+1..i-1]中的所有记录均后移一个位置;
(3)将R[i]插入到R[j+1]的位置上。
根据具体实现方法的不同可分为基于顺序查找的直接插入排序、基于折半查找的折半插入排序、基于链表存储的表插入排序、基于逐趟缩小增量的希尔排序。
直接插入排序主要利用顺序查找实现在R[1..i-1]中查找R[i]的插入位置,是一种稳定的排序算法。具体做法:R[0]作为监视哨,从R[i-1]起向前进行顺序查找,寻找R[i]的插入位置;在查找过程中找到的关键字不小于R[i].key的记录,在查找的同时,实现记录向后移动,循环结束后可以直接插入R[i]。
[13, 6, 3, 31, 9, 27, 5, 11]
第 1 次: [【13】, 6, 3, 31, 9, 27, 5, 11]
第 2 次: [【6, 13】, 3, 31, 9, 27, 5, 11]
第 3 次: [【3, 6, 13】, 31, 9, 27, 5, 11]
第 4 次: [【3, 6, 13, 31】, 9, 27, 5, 11]
第 5 次: [【3, 6, 9, 13, 31】, 27, 5, 11]
第 6 次: [【3, 6, 9, 13, 27, 31】, 5, 11]
第 7 次: [【3, 5, 6, 9, 13, 27, 31】, 11]
第 8 次: [【3, 5, 6, 9, 11, 13, 27, 31】]
一般情况下,直接插入排序的时间复杂度为
如果记录基本有序,则每次只需比较一次,将R[i]直接加入到有序序列中,最好情况下的事件复杂度为
def direct_insert_sort(R):
num = len(R)
print "第 1 次:", R[1:]
for i in range(2, num): # start sorting from the second record
R[0] = R[i]
if R[i-1] <= R[0]:
R[i] = R[0]
else:
for t in range(i): # find the place to insert R[i]
j = i-t
if R[j] > R[0]:
R[j+1] = R[j] # R[j]记录后移
if R[j-1] <= R[0]:
R[j] = R[0] # 记录R[i]插入位置j
break
print "第", i, "次:", R[1:]
return R
折半插入排序是在直接插入排序的基础上改进的插入排序算法。R[1..i-1] 是一个按关键字有序的有序序列,则可以利用折半查找实现“在R[1..i-1]中查找R[i]的插入位置”,把原来位置上的元素向后顺移。
在已形成的有序表中折半查找,比较的次数大大减少,全部元素的比较次数为
def half_insert_sort(R):
num = len(R)
for i in range(2, num):
R[0] = R[i]
if R[i-1] <= R[i]:
R[i] = R[0]
else:
# 找到位置m插入 R[i]
low = 0
high = i - 1
while low <= high:
m = (high + low)/2
if R[m] > R[0]:
high = m - 1
else:
low = m + 1
R[m+1:i+1] = R[m:i] #R[m:i]记录后移
R[m] = R[0] # insert R[i] on the record m
return R
如果记录用链表结构存储,用直接插入排序无需移动元素,时间效率更高。但是链表结构无法进行折半查找。
希尔排序的基本思想是先将整个带排序记录序列分割成若干个子序列,分别进行直接插入排序,待整个序列中的记录”基本有序“时,在对记录进行一次直接插入排序。值得注意的是子序列的构成不是简单地逐段分割,而是将相隔某个增量dk的记录组成一个子序列,让增量dk逐趟缩短(如依次取5,3,1),知道dk=1为止。
希尔排序的优点是让关键值小的元素能很快的前移,并且在序列基本有序时,在使用直接插入排序处理,时间效率会大大提高。希尔排序的时间效率根据经验公式在
def shell_sort(R):
num = len(R) - 1
print R[1:]
for dk in [5,3,1]:
if num % dk == 0:
dk_num = num/dk
else:
dk_num = num/dk + 1
for a in range(dk):
# 将dk增量记录存储到列表l,直接插入排序后赋值到列表R中,思路清晰但空间复杂度为O(n)
# 也可不单独存储为l,空间复杂度为O(1)
l = []
for i in range(dk_num):
if (1+a+dk * i) <= num:
l.append(R[1+a + dk * i])
l.insert(0, 0)
print dk, l
for i in range(2, len(l)):
l[0] = l[i]
if l[i] >= l[i-1]:
l[i] = l[0]
else:
for t in range(i):
j = i - t
if l[j] > l[0]:
l[j+1] = l[j]
if l[j-1] <= l[0]:
l[j] = l[0]
break
for i in range(len(l) - 1):
R[1+a + i * dk] = l[i + 1]
print R[1:]
return R
希尔排序的中间过程:
[13, 6, 3, 31, 9, 27, 5, 11]
第 1 次dk=5:[13, 27, 49, 55, 4, 49, 38, 65, 97, 76]#13 27排序
第 2 次dk=3:[13, 4, 49, 38, 27, 49, 55, 65, 97, 76]#13 31 5排序
第 3 次dk=1:[4, 13, 27, 38, 49, 49, 55, 65, 76, 97]#5,6,3,13,9,27,31,11排序
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!