插入排序:对一个待排记录序列,按关键字的大小插入到前面已排好序的子序列,直至全部记录插入完成。
1.直接插入排序
将A[i]插入到A[k]位置:
void InsertSort_Direct(ElemType A[ ],int n)
{
int i,k; //k表示最后插入位置
for(i=2;i<=n;i++) //对第2-n个元素遍历
{
if(A[i]<A[i-1])
{
A[0]=A[i]; //A[i]暂存到A[0]
for(k=i-1;A[k]>A[0];k--) //找到值大于A[i]的一堆元素,向后移动
A[k+1]=A[k];
k++; //找到插入位置k
A[k]=A[0]; //将A[i]的值复制A[k]
}
}
}
分析
(1)空间复杂度O(1),因为只使用了常数个辅助单元;
(2)时间效率
①最好情况:原列有序排列,比较次数为:n-1次,不需移动元素,时间复杂度:O(n);
①最坏情况:原列逆序排列, 比较次数为:0+1+2+…+n-1=n(n-1)/2次,时间复杂度:O(n^2);
(3)稳定(因为总是从后向前比较再移动,不会改变元素的相对位置);
(4)适用于顺序及链式结构。
2.折半插入排序
思想:若排序表采用顺序结构存储时,对有序子表采用折半查找,查找出待插元素位置k(=high+1),再将A[k]…A[i-1]向后移动,最后将元素A[i]插入A[k]位置。
举例如下:
void InsertSort_Binary(ElemType A[],int n)
{
int low,high; //折半查找插入位置时的指针
for(int i=2;i<=n;i++)
{
if(A[i]<A[i-1])
{
A[0]=A[i]; //将A[i]复制到A[0]
//开始进行折半查找
low=1;
high=i-1; //A[1]~A[i-1]为有序子列
while(low<=high)
{
int mid=(low+high)/2;
if(A[mid]>A[0]) //说明插入位置在mid左方
high=mid-1;
else //说明插入位置在mid右方
low=mid+1;
}
for(int j=i-1;j>=high+1;j--) //元素依次后移
A[j+1]=A[j];
A[high+1]=A[0]; //将A[i]插入
}
}
}
分析
(1)时间复杂度O(n^2);
(2)稳定。
3.希尔排序(缩小增量排序)
思想:将L[1…n]表划分为L[i,i+d,i+2d,…,i+kd]的特殊子表,分别进行直接插入排序;当整个表基本有序时,再对整个表进行一次直接插入排序。
举例分析:
分析
(1)空间复杂度O(1),因为只使用了常数个辅助单元;
(2)最坏情况:时间复杂度O(n^2);
(2)不稳定(相同的关键字被划分到不同的子表时,元素相对位置可能发生改变);
(4)适用于顺序存储。