原理
通过构建有序序列,对于未排序的数据,在已排序的序列中,从后向前扫描,找到相应位置并插入。
python实现步骤
1、默认把第一个元素看作有序序列。
2、从第二个元素开始遍历,依次将元素与上一个元素比较,如果小于,则将上一个元素向后挪一个位置;如果相等,位置想对不变。
3、继续与上上一个位置的元素比较,直到找到比它小的元素,将其插入在该元素的后面即可。(注意交换和插入动作在代码实现上的区别)
def insertion_sort1(data):
'''
直接插入排序原理:
第一层循环控制循环次数,第二层循环比较元素,并向后移动元素供待插入元素插入。
count 和 print(data)为了方便展示过程,可省略。
'''
count = 0 #统计比较次数
for i in range(1, len(data)):
temp = data[i] #temp用来存储数据
j = i-1
while j>=0 and temp<data[j]: #将当前元素与前一个元素比较
data[j+1] = data[j] #所有元素往后推一个位置
j -= 1
count += 1
data[j+1] = temp #将最大的元素放在最后面
print(data) #打印交换过程
print('循环次数:%i' % count)
return data
arr = [2,4,3,6,0,12,8]
insertion_sort1(arr)
运行结果如下:
[2, 4, 3, 6, 0, 12, 8]
[2, 3, 4, 6, 0, 12, 8]
[2, 3, 4, 6, 0, 12, 8]
[0, 2, 3, 4, 6, 12, 8]
[0, 2, 3, 4, 6, 12, 8]
[0, 2, 3, 4, 6, 8, 12]
循环次数:6
[0, 2, 3, 4, 6, 8, 12]
以上示例中,打印出了交换过程,可以看到,直接插入排序算法共循环了6次完成了整个排序工作。
算法性能
时间复杂度:O(n2);空间复杂度为O(1)。
当元素相同时,比较结束后,原来的两个元素相对位置不变,所以是稳定的排序方法。
优化
上面直接插入排序的方法需要每次将待插入元素与已排序序列中的每个元素作比较,基于这点特性,我们可以采用二分查找法来减少比较操作的次数,优化代码如下:
def insertion_sort2(arr):
'''
二分法插入排序原理:
外层循环控制循环次数,第二层确定待插入的位置与范围,最后一层循环向后挪动元素的位置,插入待插入的元素。
count 和 print(arr)为了便于观察过程,可省略。
'''
for i in range(1, len(arr)):
tem = arr[i]
left = 0
right = i-1
count = 0
#待插入的值与已排序区域的中间值比较,不断缩小区域范围,直到left和right相遇。
while left <= right:
count += 1
mid = (left+right)//2
if arr[i] < arr[mid]:
right = mid-1
else:
left = mid+1
#当left和right相遇的时候,待插入的位置其实就是left的位置,此时要将left到有序序列的最后一个元素都向后移动一个位置,才能插入元素。
#这里一定要用left-1,否则当left的位置就是有序序列的最后一个位置时,j取不到值,后面的元素会被这个位置的元素值覆盖。
for j in range(i-1, left-1 ,-1):
arr[j+1] = arr[j]
#插入元素
if left != i:
arr[left] = tem
print(arr)
print('共循环%i次' % count)
return arr
arr = [2,4,3,6,0,12,8]
insertion_sort2(arr)
运行结果如下:
[2, 3, 4, 6, 0, 12, 8]
[0, 2, 3, 4, 6, 12, 8]
[0, 2, 3, 4, 6, 8, 12]
共循环3次
[0, 2, 3, 4, 6, 8, 12]
可以看到,采用二分查找法优化后的插入排序,在一定程度上,效率还是有提升的。这次只循环了3次就找到了插入位置。