前言

每学习一个新东西总要首先知道他是什么,能做什么,怎么做,类似于哲学中的三大问题:我是谁,从哪里来,要到哪里去。或许我们一直徘徊在哲学的迷思中,也许一直想不明白,但是在思考的过程中或许会越来越接近真相,也让自己变得更强,所以进步从思考开始,我们学习算法也一样,我们得知道他是什么,思考他能解决哪些问题,具体怎么实现。那插入排序是什么呢?使用它需要满足什么条件?

何为插入排序

顾名思义,插入排序是一种简单的排序算法.所谓插入排序法就是将一个数据插入该占据的位置,比如输入一个元素,检查数组列表中的每个元素,将其插入到一个已经排好序的数列中的适当位置,使数列依然有序,当最后一个元素放入合适位置时,该数组排序完毕。综上所述,插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,该算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序算法。使用插入排序需要满足一个条件:要在一个已经有序的数据序列的基础上进行插入操作.

只要打过扑克牌的人都应该能够秒懂。它类似扑克牌的调整,插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。实现插入排序是需要反复把已排好序的元素逐步后移,为新元素提供插入空间

排序图示

说太多还是一头雾水,我们通过画图来演示或许更加直观:

比如我们有一个需要排序的序列:[5,2,4,3,1,6]

我们上面说插入排序需要在一个排好序的序列中插入,那我们就假设第一个元素,5是已经排好序的序列,然后在这个序列中操作插入排序.具体步骤如下图:

【算法实践】手把手带你快速实现插入排序_数据

这样第一轮排序就完成了,5已经归位.接下来从待排数字(未排序的蓝色部分)取出最左边的2,将他与左边已经归位的数字进行比较,如果左边的数字更大,就交换两个数字,重复该操作,直到左边已归位的数字比取出的数字更小或者取出的数字已经被移到整个序列的左边为止;

完整排序步骤如下:

【算法实践】手把手带你快速实现插入排序_算法_02

在插入排序中,需要将取出的数据与左边的数字进行比较,当左边的数字比取出的数更小的时候,就不需要继续比较,也不需要交换位置,该轮排序结束,这样的操作不断重复,直到所有的数字都排好序

如果取出的数字比左边已归位的数字都小,就必须不停地比较大小且交换位置,直到它到达整个序列的最左边为止.具体来说就是第k轮需要比较k-1次.所以最坏的情况下,第2轮需要操作1次.第3轮需要操作2次......第n轮需要操作n-1次.所以时间复杂度是O(n^2),可以看出,输入的数据是从大到小的顺序排列时就是情况最坏的情况

我找到一张更形象的动图

【算法实践】手把手带你快速实现插入排序_图解算法_03

实现流程

  1. 定义一个插入排序函数,函数参数为需要排序的序列,得到序列的长度
  2. 使用for循环依次比较列表中的数字大小,若前一个数大于后一个数,就将后面的这个数作为临时值,用temp将其存起来.将较大的这个数往后移动一位
  3. 从此处开始向前比较,找到一个小于temp的数.或者已经比较到第一个数.则将这个数之后的数据依次向后移动一个位置.temp值放到空出来的位置
  4. 验证写好的排序算法

【算法实践】手把手带你快速实现插入排序_Data_04

代码实现

直接插入排序

定义排序函数insertionSort()

def insertionSort(listData):
l = len(listData)

#从前往后比较相邻的数字,如果前一个大于后一个数,则将较小的存到temp中,较大的往后移动一位
for i in range(1, l):
j = i - 1
if(listData[i] < listData[j]):
temp = listData[i]
listData[i] = listData[j]
#判断数据应该插入的位置,后面的数据依次后移
j=j-1
while j >= 0 and listData[j]>temp:
listData[j+1] =listData[j]
j = j - 1

listData[j+1] = temp

验证排序函数:

listData = [5,2,4,3,1,6]
print('排序前数据:',listData)

insertionSort(listData)

print('排序后数据:',listData)

执行结果如下:

【算法实践】手把手带你快速实现插入排序_插入排序_05

算法改进

折半插入排序(Binary Insertion Sort)是对插入排序算法的一种改进,排序算法过程就是不断地依次将元素插入前面已排好序的序列中

排序思想:有一组数据待排序的数据,排序区间为Array[0]~Array[n-1]。将数据分为有序数据和无序数据,第一次排序时默认Array[0]为有序数据,Array[1]~Array[n-1]为无序数据。有序数据分区的第一个元素位置为low,最后一个元素的位置为high

虽然折半插入排序在查找插入点的过程优于直接插入法,但元素移动次数不变,因此复杂度依旧为:O(n^2)

在直接插入排序的基础上使用折半查找步骤:

  1. 设置第一位和当前轮末位数字的索引分别为low和high
  2. mid = int((low + hight) // 2)
  3. 如果要插入的数小于m位置的数,说明要在低半区查找,high = mid - 1
  4. 如果要插入的数大于m位置的数,说明要在高半区查找,low = mid + 1
  5. 如果要插入的数等于m位置的数,直接退出循环,low = mid
  6. 当low > high时,停止查找
  7. 插入的位置为high+1

直接上代码:

def binaryInsert(arr):
for i in range(1, len(arr)):
index = arr[i]
low = 0
high = i - 1
while low <= high:
mid = int((low + high) // 2) #取整
if index > arr[mid]:
low = mid + 1
else:
high = mid - 1
# 跳出循环后 low, mid 都是一样的, hight = low - 1
for j in range(i, low, -1):
arr[j] = arr[j - 1]
arr[low] = index
return arr

l = [5,4,7,1,8,2,3,6,9]
print(binaryInsertSort(l))

执行结果如下:

【算法实践】手把手带你快速实现插入排序_数据_06