几种常见内部排序算法分析与实现(C语言描述)

                <<几种常见内部排序算法分析与实现(C语言描述)>>
                Tags: alg,c,blog,book,linux

1. 插入排序

1.1 算法思想

插入排序算法的主要思想是:

1)  把要排序的序列分为两部分: 已排序部分(A)和未排序部分(B); 由于已排序部分和未
    排序部分有显示的位置分隔, 我们也可以将已排序部分称之为序列的前部分, 将未排
    序 部分称之为序列的后部分. 显然在算法开始前, 已排序部分为空, 未排序部分为
    整个待排序序列.
2)  从B中取第一个元素(b), 然后从后往前依次从A中取出元素(a)与b比较, 若b<a, 则把
    a往后移动一个位置; 否则, 为b找到了其正确位置(a的位置的下一个位置), 把b放入
    该位置(记住: A是已经排序好的).
3)  对A增加一个元素: b, 对B减少一个元素: b.
4)  重复 2 – 3 步骤, 直至B为空.

1.2 实现

// #c---
/*==========================================================================*
*  @Description:
*       插入排序
*
*  @Param   base
*  @Param   nmemb
*  @Param   size
*  @Param   compar
*==========================================================================*/
void Sort_insertSort(void *base, size_t nmemb, size_t size,
        int(*compar)(const void *, const void *))
{
#define SWAP(a, b, t)  ( (t)=(a), (a)=(b), (b)=(t) )

    assert(base != NULL && nmemb >= 1 && size >= 1 && compar != NULL);

    char *i, *j, t, *b, *e;
    char *bot = (char*)base;
    char *top = bot + nmemb * size;

    for ( i = bot + size; i < top; i += size )
    {
        for ( j = i; (j-=size) >= bot && compar(i, j) < 0; )
        { }
        if ( i != (j+=size) )
        {
            for ( b = j, e = i-1; b < e; b++, e-- )
                SWAP(*b, *e, t);
            for ( b = i, e = i+size-1; b < e; b++, e-- )
                SWAP(*b, *e, t);
            for ( b = j, e = i+size-1; b < e; b++, e-- )
                SWAP(*b, *e, t);
        }
    }

#undef SWAP
}
// #c---end

1.3 算法复杂度分析


从插入排序的算法思想中, 我们很容易得知其空间复杂度为 O(1), 时间复杂度为 O(N^2).

2. 选择排序

2.1 算法思想

选择排序算法的主要思想是:

1)  把要排序的序列分为两部分: 已排序部分(A)和未排序部分(B); 由于已排序部分和未
    排序部分有显示的位置分隔, 我们也可以将已排序部分称之为序列的前部分, 将未排
    序部分称之为序列的后部分. 显然在算法开始前, 已排序部分为空, 未排序部分为整
    个待排序序列.

2)  遍历B中的每一个元素, 找到B中的最小元素(b), 把b与A之后的那个元素交换.

3)  对A增加一个元素: b, 对B减少一个元素: b.

4)  重复 2 – 3 步骤, 直至B为空.

2.2 实现

// #c---
/*==========================================================================*
*  @Description:
*       选择排序
*
*  @Param   base
*  @Param   nmemb
*  @Param   size
*  @Param   compar
*
*==========================================================================*/
void Sort_selectSort(void *base, size_t nmemb, size_t size,
        int(*compar)(const void *, const void *))
{
#define SWAP(a, b, t)  ( (t)=(a), (a)=(b), (b)=(t) )
    assert(base != NULL && nmemb >= 1 && size >= 1 && compar != NULL);

    int s;
    char t, *i, *j, *min;
    char *left = base;
    char *right = left + (nmemb - 1) * size;

    for ( i = left; i <= right - size; i += size )
    {
        min = i;
        for ( j = i + size; j <= right; j += size )
        {
            if ( compar(j, min) < 0 )
                min = j;
        }
        if ( i != min )
            for ( s = 0; s < size; s++ )
                SWAP(*(i+s), *(min+s), t);
    }
#undef SWAP
}
// #c---end

2.3 算法复杂度分析

选择排序的空间复杂度为 O(1), 时间复杂度为 O(N^2).

3. 冒泡排序

3.1 算法思想

冒泡排序算法的主要思想是:

1)  把要排序的序列分为两部分: 已排序部分(A)和未排序部分(B); 由于已排序部分和未排
    序部分有显示的位置分隔, 我们也可以将已排序部分称之为序列的前部分, 将未排序
    部分称之为序列的后部分. 显然在算法开始前, 已排序部分为空, 未排序部分为整个
    待排序序列.

2)  对B进行从后往前遍历, 取所有相邻的两个元素对(b1和b2, b1较靠后, b2较靠前)作比
    较; 若b1<b2, 则交换b1和b2. 当遍历完B中所有的元素后, B中的第一个元素(b)即放
    置到了正确的位置上.

3)  对A增加一个元素: b, 对B减少一个元素: b.

4)  重复 2 – 3 步骤, 直至B为空.

3.2 实现

// #c---
/*==========================================================================*
*  @Description:
*       冒泡排序
*
*  @Param   base
*  @Param   nmemb
*  @Param   size
*  @Param   compar
*
*==========================================================================*/
void Sort_bubbleSort(void *base, size_t nmemb, size_t size,
        int(*compar)(const void *, const void *))
{
#define SWAP(a, b, t)  ( (t)=(a), (a)=(b), (b)=(t) )
    assert(base != NULL && nmemb >= 1 && size >= 1 && compar != NULL);

    int s, isSwap;
    char t, *i, *j;
    char *left = base;
    char *right = base + (nmemb - 1) * size;

    for ( i = left; i < right; i += size )
    {
        isSwap = 0;
        for ( j = right; j > i; j -= size )
        {
            if ( compar(j, j-size) < 0 )
            {
                isSwap = 1;
                for ( s = 0; s < size; s++ )
                    SWAP(*(j+s), *(j-size+s), t);
            }
        }
        if ( isSwap == 0 )
            break;
    }
#undef SWAP
}
// #c---end

3.3 算法复杂度分析

冒泡排序的空间复杂度为 O
(1), 时间复杂度为 O
(N^2).

4. 合并排序

4.1 算法思想

1)  将序列分为两个子序列.

2)  对两个子序列递归地进行合并排序.

3)  把此两个子序列
(已排序)合并为一个有序序列.

4.2 实现

// #c---
/*==========================================================================*
 * @Description:
 *      合并排序之合并例程
 *
 * @Param   left
 * @Param   center
 * @Param   right
 * @Param   size
 * @Param   compar
 * @Param   tmparray
 *
 *==========================================================================*/
static void SortST_memge(char *left, char *center, char *right, int size,
        int(*compar)(const void *, const void *), char *tmparray)
{
#define COPY_SIZE(d, s) \
    do { \
        for ( i = 0; i < size; i++ ) \
            *((d) + i) = *((s) + i); \
    } while ( 0 )

    int i;
    char *Lright = center;
    char *Rright = right;
    char *Lpcur = left;
    char *Rpcur = center + size;
    char *Tpcur = tmparray;

    for ( ; Lpcur <= Lright && Rpcur <= Rright; Tpcur += size )
    {
        if ( compar(Lpcur, Rpcur) < 0 )
        {
            COPY_SIZE(Tpcur, Lpcur);
            Lpcur += size;
        }
        else
        {
            COPY_SIZE(Tpcur, Rpcur);
            Rpcur += size;
        }
    }

    for ( ; Lpcur <= Lright; Lpcur += size, Tpcur += size )
        COPY_SIZE(Tpcur, Lpcur);
    for ( ; Rpcur <= Rright; Rpcur += size, Tpcur += size )
        COPY_SIZE(Tpcur, Rpcur);

    for ( Lpcur = left; Lpcur <= right; Lpcur += size, tmparray += size )
        COPY_SIZE(Lpcur, tmparray);

#undef COPY_SIZE
}


/*==========================================================================*
 * @Description:
 *      合并排序
 *
 * @Param   left
 * @Param   right
 * @Param   size
 * @Param   compar
 * @Param   tmparray
 *
 *==========================================================================*/
static void Sort_mergeSort_priv(char *left, char *right, int size,
        int(*compar)(const void *, const void *), char *tmparray)
{
    if ( left < right )
    {
        char *center = left + ((right - left) / size / 2) * size;

        Sort_mergeSort_priv(left, center, size, compar, tmparray);
        Sort_mergeSort_priv(center + size, right, size, compar, tmparray);
        SortST_memge(left, center, right, size, compar, tmparray);
    }
}


/*==========================================================================*
 * @Description:
*       合并排序驱动例程
 *
 * @Param   base
 * @Param   nmemb
 * @Param   size
 * @Param   compar
 *
 *==========================================================================*/
void Sort_mergeSort(void *base, size_t nmemb, size_t size,
        int(*compar)(const void *, const void *))
{
    assert(base != NULL && nmemb >= 1 && size >= 1 && compar != NULL);

    char *left = base;
    char *right = base + (nmemb - 1) * size;
    char *tmparray;

    tmparray = malloc(nmemb * size);
    if ( tmparray == NULL )
        return;

    Sort_mergeSort_priv(left, right, size, compar, tmparray);

    free(tmparray);
}
// #c---end


4.3 算法复杂度分析

归并排序算法是运用分治思想的典范, 其空间复杂度为 O(N), 时间复杂度为 O(NlogN).

5. 快速排序

5.1 算法思想

1)  从输入序列中任意选取一个元素(k)作枢纽元;

2)  遍历整个序列, 对于小于等于k的元素放在左边(L), 对于大于k的元素放在右边(R);

3)  对子序列L和R递归地进行快速排序; 直到序列中只有一个若零个元素, 退出递归.

5.2 实现

// #c---
/*==========================================================================*
*  @Description:
*       取三者间的中间值
*
*  @Param   left
*  @Param   right
*  @Param   size
*  @Param   compar
*
*  @Returns:
*==========================================================================*/
static char* Sort_median3(char *left, char *right, int size,
        int(*compar)(const void *, const void *))
{
#define SWAP(a, b, t)  ( (t)=(a), (a)=(b), (b)=(t) )
    int s;
    char t;
    // char *center = left + (right - left) / 2;    // 错误!!
    // 每一步计算都必须取整! 才不至于最后的结果位于字节内!
    char *center = left + (((right - left) / size) / 2) * size;

    if ( compar(left, center) > 0 )
        for ( s = 0; s < size; s++ )
            SWAP(*(left+s), *(center+s), t);
    if ( compar(left, right) > 0 )
        for ( s = 0; s < size; s++ )
            SWAP(*(left+s), *(right+s), t);
    if ( compar(center, right) > 0 )
        for ( s = 0; s < size; s++ )
            SWAP(*(center+s), *(right+s), t);

    for ( s = 0; s < size; s++ )
        SWAP(*(center+s), *(right-size+s), t);

    return right - size;

#undef SWAP
}

/*==========================================================================*
*  @Description:
*       快速排序
*
*  @Param   left
*  @Param   right
*  @Param   size
*  @Param   compar
*==========================================================================*/
static void Sort_quicklySort_priv(char *left, char *right, int size,
        int(*compar)(const void *, const void *))
{
#define SWAP(a, b, t)  ( (t)=(a), (a)=(b), (b)=(t) )

    int s;
    char t, *i, *j, *pivot;

    if ( left + SORT_CUTOFF * size <= right )
    {
        pivot = Sort_median3(left, right, size, compar);
        i = left;
        j = right - size;
        for ( ; ; )
        {
            for ( i += size; compar(i, pivot) < 0; i += size ) { }
            for ( j -= size; compar(j, pivot) > 0; j -= size ) { }
            if ( i < j )
            {
                for ( s = 0; s < size; s++ )
                    SWAP(*(i+s), *(j+s), t);
            }
            else
                break;
        }
        for ( s = 0; s < size; s++ )
            SWAP(*(i+s), *(right-size+s), t);

        Sort_quicklySort_priv(left, i - size, size, compar);
        Sort_quicklySort_priv(i + size, right, size, compar);
    }
    else
        Sort_insertSort(left, (right-left)/size + 1, size, compar);
#undef SWAP
}


/*==========================================================================*
*  @Description:
*       快速排序驱动例程
*
*  @Param   base
*  @Param   nmemb
*  @Param   size
*  @Param   compar
*==========================================================================*/
void Sort_quicklySort(void *base, size_t nmemb, size_t size,
        int(*compar)(const void *, const void *))
{
    assert(base != NULL && nmemb >= 1 && size >= 1 && compar != NULL);

    if ( nmemb <= 1 )
        return;

    if ( nmemb >= SORT_CUTOFF )
        Sort_quicklySort_priv(base, base + (nmemb-1)*size, size, compar);
    else
        Sort_insertSort(base, nmemb, size, compar);
}
// #c---end

5.3 算法复杂度分析

快速排序算法也是运用分治思想, 其空间复杂度为 O(1), 时间复杂度为 O(NlogN).


点赞