插入排序的基本思想:每一趟将一个待排序的记录,按其关键字的大小插入到已经排好序的一组记录的适当位置上,直到所有记录全部插入为止。a[0]不使用,用来作 监视哨,也用来避免数组下标出界。
一、直接插入排序
每一次将当前失序的关键字放入监视哨,再遍历前面已经排好序的序列,将监视哨上的关键字插入适当的位置上。
void SInsertSort(int a[],int n)//直接插入排序
{
int i,j;
for(i=2;i<=n;i++)
{
if(a[i]<a[i-1])
{
a[0]=a[i];
a[i]=a[i-1];
for(j=i-2;a[0]<a[j];j--)
{
a[j+1]=a[j];
}
a[j+1]=a[0];
}
}
return ;
}
稳定性:稳定
时间复杂度:
平均:O(n^2)
最好:O(n)
最坏:O(n^2)
空间复杂度:O(1)
既可以用顺序结构实现,也可以用链式结构实现,而对于链式结构我们不一定交换结点上的关键字进行插入排序,还可以通过改变链表指针来进行插入排序。直接插入排序适用于元素少且基本有序的情况,对于元素多且无序度较高的情况下时间复杂度较高,这时便不宜采用。
二、折半插入排序
在查找当前记录在已排好序的序列中的插入位置时,我们可以用折半查找来实现,以折半查找来实现的插入排序称之为折半插入排序。
void BInsertSort(int a[],int n)//折半插入排序
{
int i,j;
for(i=2;i<=n;i++)
{
if(a[i]<a[i-1])
{
a[0]=a[i];
int L=1,R=i-1,mid;
while(L<=R)
{
mid=(L+R)/2;
if(a[0]<a[mid])
{
R=mid-1;
}
else
{
L=mid+1;
}
}
for(j=i-1;j>=R+1;j--)
{
a[j+1]=a[j];
}
a[R+1]=a[0];
}
}
return ;
}
稳定性:稳定
时间复杂度:
平均:O(n^2)
最好:O(nlog2n)
最坏:O(n^2)
空间复杂度:O(1)
因为要使用到折半查找,所以只能用于顺序结构。对比于直接插入排序折半插入排序只减少了比较次数,而移动次数不变,所以时间复杂度与直接插入排序相同。折半插入排序适用于元素多且无序度较高的情况。
三、希尔排序
希尔排序又称缩小增量排序,首先对增量increment(increment<n)取一个整数,将整个序列按增量分成increment个组,对每一组进行直接插入排序,然后将增量increment缩小,继续进行此操作直至increment=1,此时的序列已经基本有序,再将整个序列进行直接插入排序。
void ShellSort(int a[],int n)//希尔排序
{
int in=n/2;
while(in>=1)
{
for(int i=in;i<=n;i++)
{
int j=i,temp=a[i];
while(a[j-in]>temp&&j-in>=0)
{
a[j]=a[j-in];
j-=in;
}
a[j]=temp;
}
in/=2;
}
return ;
}
稳定性:不稳定
时间复杂度:
平均:O(n^1.3)
空间复杂度:O(1)
希尔排序的第一次增量increment有许多取法,可以是increment=n/2、increment=n/3+1等,不同的增量取法会使效率有较大差异。希尔排序只能用于顺序存储结构,总的比较次数和移动次数都比插入排序要少,元素数量越多时效果越明显,因此适用于元素多且无序性大的情况。