首页 > 代码库 > [转] 插入排序
[转] 插入排序
一、直接插入排序算法:
void insertSort(int* data, int len){ int sentry;//哨兵 int i,j; for(i = 1; i < len; i ++){//从第二个元素开始排序,前面的一个元素默认有序了 if (data[i] < data[i-1]){ sentry = data[i];//复制为哨兵 for(j = i-1; sentry < data[j]; j --){ data[j+1] = data[j];//记录后移 } data[j+1] = sentry;//插入到适当的位置 } }}
直接插入排序的思想很简单,每一趟排序有序表长度加1,在给记录找出入位置的同时,后移比它大(这里针对非递减排序)的元素。分析直接插入排序算法的时空复杂度。
空间上只需要一个哨兵做辅助空间,即空间复杂度为O(1);
对每一趟排序,需要完成比较和移动两个操作,最好情况下,待排序的记录已经有序,每一趟排序只需比较1次,共∑1 = (n-1)次比较,不需要移动元素;最坏情况下,待排序的记录为逆序。比较次数共∑i = (n+2)(n-1)/2,移动共∑(i+1)=(n+4)(n-1)/2次。因此平均情况下,直接插入排序的时间复杂度为O(n^2)。
我选择用这个中途上厕所回来清牌的例子,而不用抓牌的过程,因为我认为插入排序是一个原地排序,不需要申请额外的记录表。抓牌的过程让人感觉手里的牌和还没有抓上来的牌是不同的记录表;而上厕所回来清牌,桌上乱序的牌就是一张待排序的记录表,清牌的过程是在这个表上进行的,明显没有另辟空间。
二、折半插入排序
直接插入排序中,每次查找插入位置时,都是依次往前进行比较。我们进行第i(i>1)趟排序时,前(i-1)个记录已经有序,于是查找插入位置我们可以用折半查找。算法如下:
1 void bInsertSort(int* data, int len){ 2 int sentry;//哨兵 3 int i,j; 4 int low,high,middle; 5 for(i = 1; i < len; i ++){ 6 sentry = data[i]; //复制哨兵 7 low = 0; 8 high = i - 1; 9 while(low <= high){ //寻找插入位置10 middle = (low + high) / 2; //折半下标11 if (sentry > data[middle]) //插入点在高半区12 low = middle + 1;13 else //插入点在低半区14 high = middle - 1;15 }16 for(j = i -1; j >= high + 1; j --){17 data[j+1] = data[j];//记录后移18 }19 data[j+1] = sentry;//插入到适当的位置 20 }21 }
折半插入排序只是减少了寻找插入位置而进行的比较次数,而移动记录的次数并没有减少。因此折半插入的时间复杂度仍然是O(N^2)。
三、希尔排序
不论是直接插入排序还是折半插入排序,当待排记录表很长时,排序效率会比较低,因为记录表很长时,平均每趟移动的记录数就会很多。
如上图,假设这个记录表的长度是10005,当前面1000个记录已经有序了,现在需要排第10001个记录时,发现第10001个记录需要插入到第一个位置,一共需要进行10000次移动元素。直接插入排序移动元素只能进行相邻的局部移动,不能实现跨越式的移动。
希尔排序就是从这点出发对直接插入排序进行改进。它的基本思想是:先将整个待排序的记录表分割成几个子表,对子表分别进行直接插入排序,子表的长度比较小,排序的效率会比较高。算法如下:
1 void shellInsert(int* data,int len, int dk){ 2 int sentry;//哨兵 3 int i,j; 4 for (i = dk ; i < len; i ++){ 5 if (data[i - dk] > data[i]){ 6 sentry = data[i]; 7 for(j = i - dk; j >= 0 && sentry < data[j]; j -= dk){ 8 data[j + dk] = data[j];//记录后移 9 }10 data[j + dk] = sentry;//插入到适当的位置11 }12 13 }14 }15 16 void shellSort(int* data,int len, int dlta[],int dlen){17 int k;18 for(k = 0; k < dlen; k ++){ 19 shellInsert(data,len,dlta[k]); //一趟增量为dlta[k]的插入排序 20 }21 }
希尔排序中的子序列并不是简单的逐段分割,而是将相隔某个增量的记录组成一个子序列,下图的三趟排序的增量分别为5、3、1。
我们发现在希尔排序中,较小的记录不是相邻的局部移动,而是跳跃式的移动,在最后一趟时,增量为1,待排记录表已经基本有序,利用直接插入排序即可完成排序,希尔排序的时间复杂度仍为O(n^2),但多项式系数比直接插入排序小,算法效率较高。另外希尔排序不同与直接插入排序和折半插入排序,希尔排序是不稳定的排序算法,可以考到上图中的两个相同的记录49在排序前后交换了顺序,而直接插入排序和折半插入排序是稳定的,不会改变相同记录之间的相对位置。
[转载]
http://www.cnblogs.com/fengfenggirl/archive/2013/06/02/insertSort.html