一、直接插入排序
1.概念
插入排序(InsertionSort),一般也被称为直接插入排序。
对于少量元素的排序,它是一个有效的算法。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增 1 的有序表。
在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。
2.时间复杂度
插入排序的平均时间复杂度也是 O(n^2),空间复杂度为常数阶 O(1),具体时间复杂度和数组的有序性也是有关联的。
插入排序中,当待排序数组是有序时,是最优的情况,时间复杂度为 O(N)。
最坏的情况是待排序数组是逆序的,时间复杂度是 O(n^2)。
......
就这样依次比较到最后一个元素。
代码实现:
直接插入排序
void InsertSort(int a[ ] ,int n)
{
int i,j,temp;
for(i = 1;i<n;i++) //将各元素插入已经排好的序列中
{
if(a[i]<a[i-1]){ //若选择关键字小于前驱
temp = a[i]; //用temp暂存未排序关键字
for(j = i-1;j>0&&a[j]>temp;--j) //检查所有已排序元素
a[j+1] = a[j]; //所有大于temp元素都向后移动
a[j+1] = temp; //插入到到最后位置。
}
}
}
直接插入排序(带哨兵)
void InsertSort(int a[ ] ,int n)
{
int i,j;
for(i = 2;i<=n;i++) //将各元素插入已经排好的序列中
{
if(a[i]<a[i-1]){ //若选择关键字小于前驱
a[0] = a[i]; //复制为哨兵,a[0]不存放元素
for(j = i-1;a[0]<a[j];--j) //检查所有已排序元素
a[j+1] = a[j]; //所有大于temp元素都向后移动
a[j+1] = a[0]; //插入到到最后位置。
}
}
}
二、折半插入排序
折半插入排序与直接插入排序算法原理相同。只是,在向已排序的数据中插入数据时,采用折半查找(二分查找)。
先取已经排序的序列的中间元素,与待插入的数据进行比较,如果中间元素的值大于待插入的数据,那么待插入的数据属于数组的前半部分,否则属于后半部分。依次类推,不断缩小范围,确定要插入的位置。
算法实现:
void sort(int a[ ],int n)
{
int i,j,low,mid,high;
for(i = 2;i<=n;i++)
{
a[0] = a[i];
low = 1,high = i-1;
while(low<=high){
mid = (low+high)/2;
if(a[mid]>a[0]) high =mid-1;
else low = mid+1;
}
for(j =i-1;j>=high+1;--j)
a[j+1] = a[j];
a[high+1]=a[0];
}
}
三、希尔排序
希尔排序(Shell Sort)是插入排序的一种,它是针对直接插入排序算法的改进。
希尔排序又称缩小增量排序,因 DL.Shell 于 1959 年提出而得名。
它通过比较相距一定间隔的元素来进行,各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止。
先将待排序表分割成若干子表,各对子表分别进行直接插入排序。缩小增量d,希尔本人建议每次将增量缩小一半,重复至d=1;
最后d=1时,整个表已经呈现出基本有序,再对整体进行一次直接插入排序。
希尔排序时间复杂度是 O(n^(1.3-2)),空间复杂度为常数阶 O(1)。
希尔排序没有时间复杂度为 O(n(logn)) 的快速排序算法快 ,因此对中等大小规模表现良好,但对规模非常大的数据排序不是最优选择,总之比一般 O(n^2 ) 复杂度的算法快得多。
d=1,直接退化为基本有序的直接插入排序。
算法实现:
void shellsort(int a[],int n)
{
int i,j,d;
for(d=n/2;d>=1;d=d/2) //步长变化
{
for(i = d+1;i<=n;i++) //同直接插入排序,将其步长为特值1改为变量d
{
if(a[i]<a[i-d]){
a[0]=a[i];
for(j=i-d;j>0&&a[0]<a[j];j-=d)
a[j+d]=a[j];
a[j+d] =a[0];
}
}
}
}