经典排序算法--插入排序 希尔排序 归并排序

插入排序算法

对于少量元素的排序,它是一个有效的算法。插入排序的工作方式像许多人排序一手扑克牌。开始时,我们的左手为空并且桌子上的牌面向下。然后,我们每次从桌子上拿走一张牌并将它插入到左手中正确的位置。为了找到一张牌的正确位置,我们从右到左将它与已在手中的每张牌进行比较。拿在手中的牌问题排序好的。

该算法原址排序输入的数:算法在数组A中重排这些数,在任何时候,最多只有其中的常数个数数字存储在数组外面。

//INSERTION-SORT(A)
for j = 2 to A.length
    key = A[j]
    i = j - 1
    //这个while循环对应着从右到左与手中每一张牌进行比较
    while i > 0 and key < A[i]
        A[i+1] = A[i]
        i = i - 1
    A[i+1] = key

时间复杂度
最坏情况是 Θ(n2)
平均情况也是 Θ(n2) ,因为要决定的A[1…j-1]中的哪个位置插入A[j],平均情况可能是在中间位置插入,导致平均情况和最差情况的运行时间一样,也是n的二次函数。
(平均情况往往和最坏情况一样,但是快速排序例外,后者的平均情况跟最好情况一样。)

空间复杂度
原址排序

是否是稳定排序
稳定

希尔排序

先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2

//n为数组长度 令d = 1,就变成插入排序了
void insert_sort(int a[], int n, int d)
{
    int i,j;
    for(j = d + 1;j < n; j += d)
    {
        i = j - d;
        key = a[j];
        while(i >= 0 && key < a[i])
        {
            a[i+d] = a[i];
            i -= d;
        }
        a[i+d] = key;
    }
}

int *shell_sort(int a[], int n)
{
    int d = n;
    while(d > 1){
        d = d/3 + 1;
        insert_sort(a, n, d);
    }
    return a;
}

稳定性
虽然直接插入排序是稳定的,但是shell排序是不稳定的。

效率
在效率上比直接插入排序有较大的改进。不同的增量d对希尔排序的影响比较大,例如,当增量为1时,希尔排序退化成了直接插入排序,此时的时间复杂度为 O(N2) ,而Hibbard增量的希尔排序的时间复杂度为 O(N3/2)

Hibbard增量:{1, 3, …, 2^k-1}

归并排序

虽然归并排序的运行时间是 O(nlgn) ,但从实用角来讲,它很难用于主存排序,主要问题在于 合并两个排序的表需要线性附加内存,在整个算法中还要花费将数据复制到临时数组再复制回来这样一些附加的工作,其结果严重放慢了排序的速度,所以不常用,而是用归并排序的思想来实现外部排序。

首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。

可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。

归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)。因为归并排序每次都是在相邻的数据中进行操作,所以归并排序在O(N*logN)的几种排序方法(快速排序,归并排序,堆排序)也是效率比较高的。

//2 归并排序
void _merge(int a[], int p, int q,int r, int *temp)
{
    int i,j,k;
    i=p;j=q+1;k=0;
    while(i <= q && j<= r)
    {
        if(a[i]<a[j])
            temp[k++] = a[i++];
        else
            temp[k++] = a[j++];
    }
    while(i <= q)
    {
        temp[k++] = a[i++];
    }
    while(j<= r)
    {
        temp[k++] = a[j++];
    }
    for(i = 0;i<k;i++)
    {
        a[p+i] = temp[i];
        //printf("%d ",temp[i]);
    }
    //printf("\n");

}
void  _merge_sort(int a[], int p, int r, int *temp)
{
    if(p<r)
    {
        int q;
        q = (p+r)/2;
        _merge_sort(a,p,q, temp);
        _merge_sort(a,q+1,r,temp);
        _merge(a,p,q,r,temp);
    }

}
int *merge_sort(int a[], int n)
{
    int *temp = (int*)malloc(sizeof(int)*n);
    _merge_sort(a,0,n-1, temp);
    free(temp);
}

时间复杂度 O(nlog2(n)
空间复杂度 O(n)
稳定性 稳定

点赞