一、插入排序的主要分类:


1、直接插入排序;



2、折半插入排序;


3、2-路插入排序;


4、希尔排序(缩小增量排序)。


二、直接插入排序(简便,容易实现):


1、排序过程:整个排序过程为n-1趟插入,即先将序列中第1个元素看成是一个有序子序列,然后从第2个元素开始,逐个进行插入,直至整个序列有序。


2、算法实现:



void InsertSort(int a[], int len)
{
for (int i = 1; i < len; ++i)
{
int temp = a[i]; //先把将要插入的元素保存
int j = i;
while (j > 0 && temp < a[j - 1])//通过while循环找出应该插入的位置
{
a[j] = a[j - 1]; //后移
--j;
}
a[j] = temp;
}
}


3、算法评价:


(1) 时间复杂度


——若待排序元素按关键字从小到大排列(正序)

关键字比较次数:n-1


元素移动次数:0


——若待排序元素按关键字从大到小排列(逆序)


关键字比较次数:n*(n-1)/2     注意:上面算法并没设置哨兵,若有设置,则比较次数应为(n+2)(n-1)/2


元素移动次数:n*(n-1)/2     注意:上面算法并没设置哨兵,若有设置,则移动次数应为(n+4)(n-1)/2



——若待排序元素是随机的,取平均值


关键字比较次数:(n+2)(n-1)/4


元素移动次数:n*(n-1)/4


故时间复杂度为:T(n)=O(n²)


(2)空间复杂度:S(n)=O(1)


4、适应范围:当序列中元素的个数n很少时。



三、折半插入排序(减少了元素的比较次数):


1、排序过程:用折半查找方法确定插入位置。


2、算法实现:

void binsort(int a[], int len)
{
for (int i = 1; i < len; ++i)
{
int temp = a[i];
int low = 0;
int high = i - 1; //a[0]至a[i-1]的已经排序好了。
//执行while循环(折半查找)找出a[i]应该插入的位置
while (low <= high)
{
int middle = (low + high) / 2;
if (temp < a[middle])
high = middle - 1;
else
low = middle + 1;
}
//a[low]至a[i-1]的元素移动到a[low+1]至a[i];
for (int j = i; j > low; --j)
{
a[j] = a[j - 1];
}
a[low] = temp;//a[low]放置插入的元素
}
}


3、算法评价:


(1) 时间复杂度:


折半插入排序减少了关键字比较的次数,而元素的移动次数不变,故时间复杂度仍然为:T(n)=O(n²)






(2)空间复杂度:S(n)=O(1)


4、适应范围:当序列中元素的个数n很大时。


四、2-路插入排序(减少了元素的移动次数,但是增加了空间复杂度):


1、排序过程:引进一个空序列b。把原序列中的第一个元素a[0]看成是处于排好后的序列的中间。大于a[0]的元素放于a[0]值前的有序序列中,反之,放于其后。


如下图例子:





2、算法实现: 

void TowRsort(int a[], int len)
{
int *b = new int[len];
int current = len; //数组b后面序列中下标最小的一个
b[0] = a[0];//在b数组中放置第一个元素
int k = 0;
for (int i = 1; i < len; ++i){
if (a[i] >= b[0]){
int k1 = k;
//找出应该插入的位置
while (a[i] < b[k1]){

b[k1 + 1] = b[k1];
--k1;
}
b[k1 + 1] = a[i];
++k;
} else{
if (current == len)//放置第一个在数组最后
b[--current] = a[i];
else{
int tempCurrent = current;
//找出应该插入的位置
while (b[tempCurrent] < a[i] && tempCurrent < len){
b[tempCurrent - 1] = b[tempCurrent];
++tempCurrent;
}
b[tempCurrent - 1] = a[i];
--current;
}
}
}
int j = 0;
for (int i = current; i < len; ++i, ++j){
a[j] = b[i];
}
for (int i = 0; i < current; ++i, ++j){
a[j] = b[i];
}
}



3、算法评价:


(1) 时间复杂度:时间复杂度仍然为:T(n)=O(n²)。(然而元素的移动次数约n*n/8)






(2)空间复杂度:S(n)=O(n)。


五、希尔插入排序(缩小增量法)(减少了元素的比较次数):



1、排序过程:分割成若干个较小的子文件,对各个子文件分别进行直接插入排序,当文件达到基本有序时,再对整个文件进行一次直接插入排序。


如下图例子:



2、算法实现:

void shellSort(int a[],const int& len){//希尔排序
int b[] = {5,3,1};//增量序列
int count = sizeof(b)/sizeof(int);
for(int i = 0; i < count; ++i){
for(int j = 0; j < len - b[i]; ++j){
if(a[j] > a[j+b[i]]){
swap(a[j],a[j+b[i]]);//交换a[j]和a[j+b[i]]的值
int temp = j;
while(temp >= b[i]){//向前想间隔b[i]的比较
if(a[temp] < a[temp-b[i]]){
swap(a[temp],a[temp-b[i]]);//交换
}
else//只要有一个不满足,就跳出
break;
temp -= b[i];//向前退
}
}
}
}
}


3、算法评价:


(1) 时间复杂度:在时间效率上较上述几种排序方法有较大的提高。时间复杂度效率介于O(n)与O(n*n)之间。


(2)空间复杂度:S(n)=O(1)。


4、补充说明:希尔排序的分析一个复杂的问题,因为它的时间复杂度与所取的增量序列有关,涉及到了数学上一些尚未解决的问题。因此到目前都没有人求出最好的增量序列。但是,应该使增量序列中没有除1外的公因子,并且最后一个增量值必须为1。


 

本文若有错误或者不足之处,请您不吝指正。谢谢。