插入排序及其优化

插入排序

复杂度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、重复元素越多也间接表现为有序性强;越短表现为有序性越强,所以插入排序会被当做子过程优化其他排序算法。

猜你喜欢

转载自blog.csdn.net/qq_27366789/article/details/82955557