插入排序核心:通过构建有序序列,对于未排序序列,在已排序序列中从后向前扫描(对于单向链表则只能从前往后遍历),找到相应位置并插入。实现上通常使用in-place排序(需用到O(1)的额外空间)
- 从第一个元素开始,该元素可认为已排序
- 取下一个元素,对已排序数组从后往前扫描
- 若从排序数组中取出的元素大于新元素,则移至下一位置
- 重复步骤3,直至找到已排序元素小于或等于新元素的位置
- 插入新元素至该位置
重复2~5
按照以上分析,我们用python语言可以写出插入排序的代码:
def insertSort(li):
#插入排序,核心思想是对每一个元素按照从后向前的顺序依次比较大小
n = len(li)
if n <= 1:
return li
else:
for i in range(1, n):
value = li[i]
index = i
while li[index-1] > value and index > 0:
li[index] = li[index - 1]
index -= 1
li[index] = value
return li
在作者电脑上通过对长度为20000的列表进行排序,计算时长为49s。
简单的优化:
通过对插入算法的分析,我们发现,在每次对已知元素与前面的元素进行比较大小时,如果小于前面的,我们就将两者调换位置。这很类似在图书馆放置图书的过程,我们在将借阅的图书放置回原来位置时,是直接锁定该位置,然后将该位置一侧的图书全部移动一部分,最后再将该图书直接插入到该位置上,而不是一本一本的移动。
从这个思想出发,我们可以对插入算法进行适当优化:在while循环中的比较我们并不做元素的交换,只用来记录索引的位置。当确定该元素需要插入的位置后,我们直接将该元素插入到指定位置即可,这样可以省下很多次元素的交换。
通过以上分析,我们可以对源代码进行修改:
def insertSort(li):
#插入排序,核心思想是对每一个元素按照从后向前的顺序依次比较大小
n = len(li)
if n <= 1:
return li
else:
for i in range(1, n):
value = li[i]
index = i
while li[index-1] > value and index > 0:
index -= 1
res = li.pop(i)
li.insert(index, res)
return li
这里我们通过list.pop()方法移除目标元素,通过list.insert()方法将目标元素插入到指定位置。
同样对长度为20000的列表进行排序,计算时长为29s,节省了40%的时长。