插入排序:Python语言实现
插入排序的时间复杂度也是 ,但原理稍有不同。它在列表较低的一端维护一个有序的子列表,并逐个将每个新元素“插入”这个子列表。下图展示了插入排序的过程。深色元素代表有序子列表中的元素。
首先假设位置 0 处的元素是只含单个元素的有序子列表。从元素 1 到元素 n–1,每一轮都将当前元素与有序子列表中的元素进行比较。在有序子列表中,将比它大的元素右移;当遇到一个比它小的元素或抵达子列表终点时,就可以插入当前元素。
下图详细展示了第 5 轮遍历的情况。此刻,有序子列表包含 5 个元素:17、26、54、77 和 93。现在想插入 31。第一次与 93 比较,结果是将 93 向右移;同理,77 和 54 也向右移。遇到 26 时,就不移了,并且 31 找到了正确位置。现在,有序子列表有 6 个元素。
从下面代码可知,在给 n 个元素排序时,插入排序算法需要遍历 n–1 轮。循环从位置 1 开始,直到位置 n–1 结束,这些元素都需要被插入到有序子列表中。
def insertion_sort(alist):
for index in range(1, len(alist)):
currentvalue = alist[index]
position = index
while position > 0 and alist[position-1] > currentvalue:
alist[position] = alist[position-1]
position = position-1
alist[position] = currentvalue
测试代码:
a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20]
print(f"before sorting: {a_list}")
insertion_sort(a_list)
print(f"after sorting: {a_list}")
assert a_list == sorted(a_list)
输出:
before sorting: [54, 26, 93, 17, 77, 31, 44, 55, 20]
after sorting: [17, 20, 26, 31, 44, 54, 55, 77, 93]
性能测试:
from random import randint, seed
seed(13)
lst_to_sort = [randint(100, 999) for _ in range(1000)]
%timeit insertion_sort(lst_to_sort)
结果:
61.1 μs ± 1.2 μs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
在最坏情况下,插入排序算法的比较次数是前 n–1 个整数之和,对应的时间复杂度是 。在最好情况下(列表已经是有序的),每一轮只需比较一次。
移动操作和交换操作有一个重要的不同点。总体来说,交换操作的处理时间大约是移动操作的 3 倍,因为后者只需进行一次赋值。在基准测试中,插入排序算法的性能很不错。
参考文档
《Python数据结构与算法分析(第2版)》:5.3.3 插入排序