插入排序
复杂度O(n**2),用笔模拟过程可轻松得出
一、思路
index 0 1 2 3 4 5
value 4 3 2 6 4 8
1、选定起始指针index=0,此时已排好顺序的子数组长度为1(就是list[0]==4)
2、指针后移,候选元素变为list[1]==3,将该元素不断向左交换到某个位置,使得list[0]与list[1]组成的子数组有序
3、指针继续后移,直到抵达数组尾,向左不断交换位置到合适位置后,排序结束
二、实现
推荐insert_sort1的实现;第二种使用for in 语句在某些情况下具有天生缺陷(无后缀++/–操作)
from sort_helper import test_sort,random_array
def insert_sort1(array):
for i in range(len(array)):
#当前位置i代表可以向左交换位置的次数
j = i
while j:
if array[j]<array[j-1]:
array[j],array[j-1] = array[j-1],array[j]
j -= 1
else:
break
#技巧性比较强,且range用来遍历比较好,循环的话有固有缺陷,无后缀+/后缀-
def insert_sort2(array):
#第0个元素选取后就排好序了,所以直接从1开始计数
for i in range(1,len(array)):
#注意交换位置时,j最小为1,使用下面的逆序非常合适
for j in range(i,0,-1):
if array[j] < array[j-1]:
array[j],array[j-1] = array[j-1],array[j]
if __name__ == '__main__':
array = random_array(1000, 10, 50)
test_sort('insert_sort1', insert_sort1, array)
test_sort('insert_sort2', insert_sort2, array)
- 代码精简技巧:
下面两段代码意义相同,先实现,再精简!!
#代码1表明:仅有exp2成立时,循环才可以继续,所以可以把exp2直接提到while判断语句
while exp1:
if exp2:
pass
else:
break
#代码2
while exp1 and exp2:
pass
三、插入排序与选择排序对比:均为O(n**2)的排序算法
from selectionsort1 import selectionsort1
from insert_sort import insert_sort2
from sort_helper import test_sort,random_array
if __name__ == '__main__':
array = random_array(10000, 0, 10000)
array1 = array[:]
test_sort('selectionsort', selectionsort1, array)
test_sort('insertsort', insert_sort2, array1)
可以看出:插入排序居然比选择排序慢,要知道选择排序的第二轮循环遍历了一次,插入排序第二轮循环有提前终止的情况
**因为:插入排序第二轮循环实现方式略差,最坏情况下要switch i次,而选择排序找到该轮最小值时仅switch1次;每switch1次涉及3次赋值,耗时较大
3.1 插入排序算法改进1
申请一个临时变量,保存当前候选元素,
#改进版插入排序
def insert_sort3(array):
for i in range(len(array)):
#当前位置i代表可以向左交换位置的次数
j = i
temp = array[i]
while j:
if temp < array[j-1]:
array[j] = array[j-1]
j -= 1
else:
break
array[j] = temp
- 随机数组测试
from selectionsort1 import selectionsort1
from insert_sort import insert_sort2,insert_sort3
from sort_helper import test_sort,random_array
if __name__ == '__main__':
array = random_array(10000, 0, 10000)
array1 = array[:]
array2 = array[:]
test_sort('selectionsort', selectionsort1, array)
test_sort('insertsort', insert_sort2, array1)
test_sort('advanced insertsort', insert_sort3, array2)
可以看出改进后性能提升明显(但还是比选择排序慢,python问题?)
3.2 插入排序对于近乎有序的数组
- 近乎有序数组发生器
def nearly_ordered_array(n,switch_num):
myarr = list(range(n))
for _ in range(switch_num):
a = randint(0, n-1)
b = randint(0, n-1)
myarr[a],myarr[b] = myarr[b],myarr[a]
return myarr
- 测试
from selectionsort1 import selectionsort1
from insert_sort import insert_sort2,insert_sort3
from sort_helper import test_sort,random_array,nearly_ordered_array
if __name__ == '__main__':
array = nearly_ordered_array(10000, 10)
array1 = array[:]
array2 = array[:]
test_sort('selectionsort', selectionsort1, array)
test_sort('insertsort', insert_sort2, array1)
test_sort('advanced insertsort', insert_sort3, array2)
这种情况下,改进后的选择排序算法性能优势异常明显
- 总结:
1、selectionsort和insertsort时间复杂度均为O(n**2)
2、选择排序性能较为稳定
3、对于有序性较强的数组,选择排序性能优势明显;对于完全有序的数组,选择排序退化为O(n),但选择排序仍为O(n**2)
4、重复元素越多也间接表现为有序性强;越短表现为有序性越强,所以插入排序会被当做子过程优化其他排序算法。