插入排序的做法是在循环遍历整个待排序序列时,每遍历一个元素,就会将此元素插入到一个已排好序的列表中,待整个序列遍历完毕,则已排好序的列表就是排序的结果,另外已排好序的列表并不需要额外的存储空间,也就是可以进行原址排序。
具体做法如下:
有一序列长度为8
4 | 6 | 1 | 3 | 9 | 5 | 8 | 2 |
从第一个元素i = 0开始遍历,取到4,而已排序列表(设为A)此时为空,直接将此元素插入A中即可,黄色的代表已排好序的列表,i++;
4 | 6 | 1 | 3 | 9 | 5 | 8 | 2 |
继续遍历i = 1,此时元素为6,往已排序列表中插入,结果如下,i++
4 | 6 | 1 | 3 | 9 | 5 | 8 | 2 |
继续遍历i = 2,此时元素为1,往已排序列表中插入,此时要注意,要将1插入到A中,要将比1大的元素一个个依次往后挪,结果如下,i++
1 | 4 | 6 | 3 | 9 | 5 | 8 | 2 |
按以上流程依次进行,直接将所有元素遍历,得到的结果就是已排好序的结果了
1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 |
1 void InsertSort(int num[], int nSize){ 2 for(int i = 0;i < nSize;++i){ 3 int j = i - 1; 4 int tobeInsert = num[i]; 5 6 //以下是在已排好序的列表中,查找可以插入的位置 7 while(j >= 0 && num[j] > tobeInsert){ 8 //执行到此处的都表示,比待插入元素要大,要将此元素往后挪 9 num[j + 1] = num[j]; 10 j = j - 1; 11 } 12 //while循环结束后,要么是j = -1,表示没有找到任何元素比待插入的元素小,要么是找到了一个元素比待插入元素小的位置 13 //如果是j = -1,则说明待插入元素是已排好序中最小的了,则直接插入到0位置即可 14 //如果是第二种情况,则将待插入元素插入到找到的元素的后面 15 //结合上面两种情况,用以下代码就能概括了 16 num[j + 1] = tobeInsert; 17 } 18 }
其实以上代码,i从1直接开始就行了,因为i=0时,已排序的列表为空,不需要查找位置的过程。
插入排序的时间复杂度为O(N 2)
按算法导论第二章的定义,A按序排序的性质,称为循环不变式,循环不变式是用来帮助我们理解算法的正确性的,关于循环不变式,需要证明以下三条性质
是对的。
初始化:循环的第一次迭代之前,它为真
保持:如果循环的某次迭代之前它为真,那么下次迭代之前它仍为真
终止:在循环终止时,不变式为我们提供一个有用的性质,该性质有助于证明算法是正确的。
对以上算法,当i = 0时,A为空,当i = 1时,A只有一个元素,循环不变式都是成立的。所以初始化是正确的。
保持:其后每一次迭代,新的元素插入时,都将A中的元素比新插入元素大的元素往后挪,保持了A的按序排列
终止:当i = nSize - 1时,A中的元素个数为nSize - 1,此都已经按序排序,此时将i指向的元素插入A中,A的元素为nSize
当i = nSize,循环中止,A的元素个数和nSize,且都已按序排列,这就是我们想要的排序结果,说明算法正确 .