20191209-八大排序之插入排序
1. 插入排序
算法核心思想
插入排序的核心思想是将数组中所有的元素分别和前面已经排序好的元素相比较,如果后面选择的元素比已排序的元素小,则交换位置,直至比较完成。具体逻辑如下:
- 取数组的第一个元素为已经排序好的元素,将第一个元素看作有序序列
- 取数组的第二个元素和已经排序号的元素进行比较,如果第二个元素比第一个元素小,则交换位置,排序完成后第一个元素和第二个元素必然有序,形成新的有序数列
- 取数组的第三个元素,依次和第一个第二个元素进行比较,排序完成后第一个,第二个,第三个元素形成一个有序数列
- 取最后一个元素重复上述步骤,最终实现整个数组有序
代码实现
while版本
def myInsertSort(arr): for i in range(1,len(arr)):#默认第0号索引的元素是一个已经排好序的序列,所以从第一个元素开始 j = i#此时arr[:j]是一个已经排好序的序列,使用arr[i](arr[j+1])和arr[:j]依次作比较 while j>=1: if arr[j-1]>arr[j]: arr[j-1],arr[j] = arr[j],arr[j-1] else: break j-=1 return arr s = [93,28,1,1,2,3,4,5,6,2,3,4,5,-99,-98,-99,0] print(myInsertSort(s))
嵌套for版本
def myInsertSort(arr): for i in range(1,len(arr)):#默认第0号索引的元素是一个已经排好序的序列,所以从第一个元素开始 for j in range(i-1,-1,-1):#此时arr[:j]是一个已经排好序的序列,使用arr[i](arr[j+1])和arr[:j]依次作比较 if arr[j+1]<arr[j]: arr[j+1],arr[j] = arr[j],arr[j+1] else: break return arr s = [93,28,1,1,2,3,4,5,6,2,3,4,5,-99,-98,-99,0] print(myInsertSort(s))
执行解析
外层for遍历读取第n个元素,当读取第n个元素的时候前面n-1个元素是已经排序好的序列,使用第n个元素和前面的n-1个元素进行依次比较,如果某一个元素比较的时候比第n个元素大,则交换该元素和第n个元素的位置,这样比较轮下来从第0个元素到第n个元素就是一个有序序列了。
复杂度分析
在最好的情况下,即序列已经是排好序的情况下,每次比较一次就退出while循环,则总比较次数是n-1次,时间复杂度是O(n)
在最坏的情况下,即每次while循环都要比较到第一个元素,则:
第一次循环,比较了1次;
第二次循环,比较了2次;
...
第n-1次循环,比较了n-1次;
总的比较次数是1+2+3+...+(n-1) = n(n-1)/2。
我们上面所求得的n(n-1)/2,其时间复杂度,最大的影响因子是n^2/2,故其时间复杂度是O(n^2)。
总结
- 在大多数元素已经有序的情况下,插入排序的工作量较小,这个结论很明显,如果一个数组大部分元素都有序,那么数组中的元素自然不需要频繁地进行比较和交换。
- 在元素数量较少的情况下,插入排序的工作量较小,这个结论更加显而易见,插入排序的工作量和n的平方成正比,如果n比较小,那么排序的工作量自然要小得多。希尔排序
- 插入排序是一种稳定排序