排序算法的c实现示例

c库排序算法只有快速排序qsort的实现,效率较高,非递归。
也没有必要采用其它排序算法。

基于网上资料(都是对int数组进行排序),本版本的实现,全部改为类似qsort的实现,适合任意结构的排序。

排序算法列表:
冒泡
选择
插入
希尔
归并
快速排序(递归版本)
快速排序(glibc版本)
快速排序(c语言自带版本直接调用qsort)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <memory.h>

typedef enum{
    false = 0,
    true = 1
}bool; //布尔类型

typedef struct
{
    //测试用结构体
    int key;
    int data;
}TestStruct;

//p[i]访问宏
#define VEC(p, i, type_size) ((void*)(((char*)(p))+(type_size)*(i)))

//比较函数的函数指针类型定义
typedef int(*COMPAR)(const void*, const void*);
typedef int(*COMPAR_d)(const void*, const void*, void*);

//比较函数
int cmp_key_1(const void *l, const void *r)
{
    return *((long*)l) - *((long*)r);
}
int cmp_key_2(const void *l, const void *r)
{
    if (*((long*)l) != *((long*)r)) {
        return *((long*)l) - *((long*)r);
    }
    else {
        return ((long*)l)[1] - ((long*)r)[1];
    }
}

//内存释放
void free_data(void **p)
{
    if (NULL != *p) {
        free(*p);
        *p = NULL;
    }
    return;
}

//随机初始化一个任意集合,只初始化每个元素的第一个4字节
bool rand_ini_set(void *set, long cnt, size_t type_size)
{
    if (set == NULL || cnt <= 0 || type_size < 4)
    {
        return false;
    }
    srand((unsigned int)time(0));
    for (int i = 0; i < cnt; ++i)
    {
        *(int*)(VEC(set, i, type_size)) = rand();
    }
    return true;
}

//打印输出任意集合的首4字节,作为int关键字输出
bool print_set_key(void *set, long cnt, size_t type_size, int count_per_line)
{
    if (set == NULL || cnt <= 0 || type_size < 4)
    {
        return false;
    }
    int iLine = cnt / count_per_line + (((cnt % count_per_line)>0)? 1:0);
    int iSetItem = 0;
    for (int i = 0; i < iLine; ++i)//行
    {
        for (int j = 0; j < count_per_line; ++j)
        {
            if (iSetItem == cnt)
            {
                break;
            }
            //unsigned int 0 - 4294967295
            //rand产生一个0-0x7fff的随机数,即最大是32767的一个数
            int iTemp = *(int*)(VEC(set, iSetItem, type_size));
            if (j == count_per_line - 1)
            {
                //printf("%011d", iTemp);
                printf("%05d", iTemp);
            }
            else
            {
                //printf("%011d ", iTemp);
                printf("%05d ", iTemp);
            }
            ++iSetItem;
        }
        printf("\n");
    }
    printf("\n");
    return true;
}

#define TestSetSize (100)
#define CountPerLine (10)

void bubble_sort(void *set, int cnt, size_t type_size, COMPAR f);//冒泡
void selection_sort(void *set, int cnt, size_t type_size, COMPAR f);//选择
void insertion_sort(void *set, int cnt, size_t type_size, COMPAR f);//插入
void shell_sort(void *set, int cnt, size_t type_size, COMPAR f);//希尔
void merge_sort(void *set, int cnt, size_t type_size, COMPAR f);//归并
//void qsort(void *set, int cnt, size_t type_size, COMPAR f); //c标准库自带的。
void qsort2(void *set, int cnt, size_t type_size, COMPAR f);//递归实现快速排序
void qsort_glibc(void *const pbase, size_t total_elems, size_t size, COMPAR cmp);//从glibc库中移植改写的快速排序

int main()
{
    TestStruct *pSet = malloc(sizeof(TestStruct) * TestSetSize);
    rand_ini_set(pSet, TestSetSize, sizeof(TestStruct));
    print_set_key(pSet, TestSetSize, sizeof(TestStruct), CountPerLine);
    //
    //bubble_sort(pSet, TestSetSize, sizeof(TestStruct), cmp_key_1);
    //selection_sort(pSet, TestSetSize, sizeof(TestStruct), cmp_key_1);
    //insertion_sort(pSet, TestSetSize, sizeof(TestStruct), cmp_key_1);
    //shell_sort(pSet, TestSetSize, sizeof(TestStruct), cmp_key_1);
    //merge_sort(pSet, TestSetSize, sizeof(TestStruct), cmp_key_1);
    //qsort(pSet, TestSetSize, sizeof(TestStruct), cmp_key_1);
    //qsort2(pSet, TestSetSize, sizeof(TestStruct), cmp_key_1);
    qsort_glibc(pSet, TestSetSize, sizeof(TestStruct), cmp_key_1);
    //
    print_set_key(pSet, TestSetSize, sizeof(TestStruct), CountPerLine);

    return 0;
}

void swap(void *x, void *y, size_t type_size, void *temp) {
    memcpy(temp, x, type_size);
    memcpy(x, y, type_size);
    memcpy(y, temp, type_size);
}
#define SWAP_MEM(x,y,type_size,temp) memcpy(temp, x, type_size);memcpy(x, y, type_size);memcpy(y, temp, type_size)

/*冒泡排序(英语:Bubble Sort):
重复地走访过要排序的数列,
一次比较两个元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。*/
void bubble_sort(void *set, int cnt, size_t type_size, COMPAR f) {
    //冒泡排序
    int i, j;
    void *temp = malloc(type_size);
    if (NULL == temp)return;
    for (i = 0; i < cnt - 1; i++){
        for (j = 0; j < cnt - 1 - i; j++){
            if (f(VEC(set, j, type_size), VEC(set, j + 1, type_size)) > 0) {
                memcpy(temp, VEC(set, j, type_size), type_size);
                memcpy(VEC(set, j, type_size), VEC(set, j + 1, type_size), type_size);
                memcpy(VEC(set, j + 1, type_size), temp, type_size);
            }
        }
    }
    free_data(&temp);
}

/*选择排序(Selection sort):
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
以此类推,直到所有元素均排序完毕。*/
void selection_sort(void *set, int cnt, size_t type_size, COMPAR f)
{
    //选择排序
    int i, j;
    void *temp = malloc(type_size);
    if (NULL == temp)return;
    for (i = 0; i < cnt - 1; i++)
    {
        int min = i;
        for (j = i + 1; j < cnt; j++){     //走訪未排序的元素
            if (f(VEC(set, j, type_size), VEC(set, min, type_size)) < 0){    //找到目前最小值
                min = j;    //紀錄最小值
            }
        }
        //做交換
        memcpy(temp, VEC(set, min, type_size), type_size);
        memcpy(VEC(set, min, type_size), VEC(set, i, type_size), type_size);
        memcpy(VEC(set, i, type_size), temp, type_size);
    }
    free_data(&temp);
}

/*插入排序(英语:Insertion Sort):
工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
插入排序在实现上,通常采用in-place排序(即只需用到 {\displaystyle O(1)} {\displaystyle O(1)}的额外空间的排序),
因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
*/
void insertion_sort(void *set, int cnt, size_t type_size, COMPAR f){
    //插入排序
    int i, j;
    void *temp = malloc(type_size);
    if (NULL == temp)return;
    for (i = 1; i < cnt; i++){
        memcpy(temp, VEC(set, i, type_size), type_size);
        for (j = i; j > 0 && (f(VEC(set, j - 1, type_size), temp) > 0); --j)
        {
            memcpy(VEC(set, j, type_size), VEC(set, j - 1, type_size), type_size);
        }
        memcpy(VEC(set, j, type_size), temp, type_size);
    }
    free_data(&temp);
}

/*希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位*/
void shell_sort(void *set, int cnt, size_t type_size, COMPAR f) {
    //希尔排序
    int gap, i, j;
    void *temp = malloc(type_size);
    if (NULL == temp)return;
    for (gap = cnt >> 1; gap > 0; gap = gap >> 1)
    {
        for (i = gap; i < cnt; i++)
        {
            memcpy(temp, VEC(set, i, type_size), type_size);
            for (j = i - gap; j >= 0 && (f(VEC(set, j, type_size), temp) > 0); j -= gap)
            {
                memcpy(VEC(set, j + gap, type_size), VEC(set, j, type_size), type_size);
            }
            memcpy(VEC(set, j + gap, type_size), temp, type_size);
        }
    }
    free_data(&temp);
}

/*归并排序:把数据分为两段,从两段中逐个选最小的元素移入新数据段的末尾。
可从上到下或从下到上进行。
*/
void merge_sort(void *set, int cnt, size_t type_size, COMPAR f) {
    //归并排序
    void *a = set;
    void *b = malloc(cnt * type_size);
    if (NULL == b)return;
    int seg, start;
    for (seg = 1; seg < cnt; seg += seg) {
        for (start = 0; start < cnt; start += seg + seg) {
            int low = start;
            int mid = min(start + seg, cnt);
            int high = min(start + seg + seg, cnt);
            int k = low;
            int start1 = low;
            int end1 = mid;
            int start2 = mid;
            int end2 = high;
            while (start1 < end1 && start2 < end2)
            {
                if (f(VEC(a, start1, type_size), VEC(a, start2, type_size)) < 0)
                {
                    memcpy(VEC(b, k, type_size), VEC(a, start1, type_size), type_size);
                    ++start1;
                }
                else
                {
                    memcpy(VEC(b, k, type_size), VEC(a, start2, type_size), type_size);
                    ++start2;
                }
                ++k;
            }
            while (start1 < end1)
            {
                memcpy(VEC(b, k, type_size), VEC(a, start1, type_size), type_size);
                ++k;
                ++start1;
            }
            while (start2 < end2)
            {
                memcpy(VEC(b, k, type_size), VEC(a, start2, type_size), type_size);
                ++k;
                ++start2;
            }
        }
        void *temp = a;
        a = b;
        b = temp;
    }
    if (a != set) {
        memcpy(b, a, type_size * cnt);
        b = a;
    }
    free_data(&b);
}

/*快速排序:在区间中随机挑选一个元素作基准,将小于基准的元素放在基准之前,大于基准的元素放在基准之后,再分别对小数区与大数区进行排序。*/

// 确定快速排序中间值那个位置,用来进行比较
// 左边的值 < 右边的值
int partition(int *set, int l, int r, size_t type_size, COMPAR f)
{
    int i, j;
    i = l - 1;
    void *temp = malloc(type_size);
    for (j = l; j < r; j++)
    {
        if (f(VEC(set, j, type_size), VEC(set, r, type_size)) <= 0)
        {
            i++;
            SWAP_MEM(VEC(set, i, type_size), VEC(set, j, type_size), type_size, temp);
        }
    }
    SWAP_MEM(VEC(set, i + 1, type_size), VEC(set, r, type_size), type_size, temp);
    free_data(&temp);
    return i + 1;
}

// 快速排序算法, 递归
void quick_sort_recursion(void *set, int l, int r, size_t type_size, COMPAR f)
{
    if (l >= r)
        return;
    int mid = partition(set, l, r, type_size, f);
    quick_sort_recursion(set, l, mid - 1, type_size, f);
    quick_sort_recursion(set, mid + 1, r, type_size, f);
}

void qsort2(void *set, int cnt, size_t type_size, COMPAR f)
{
    quick_sort_recursion(set, 0, cnt - 1, type_size, f);
}

#define MAX_THRESH 4
#define SWAP(a, b, size)						      \
  do									      \
        {									      \
      register size_t __size = (size);					      \
      register char *__a = (a), *__b = (b);				      \
      do								      \
            	{								      \
	  char __tmp = *__a;						      \
	  *__a++ = *__b;						      \
	  *__b++ = __tmp;						      \
            	} while (--__size > 0);						      \
        } while (0)
typedef struct
{
    char *lo;
    char *hi;
} stack_node;
#define STACK_SIZE	(CHAR_BIT * sizeof(size_t))
#define PUSH(low, high)	((void) ((top->lo = (low)), (top->hi = (high)), ++top))
#define	POP(low, high)	((void) (--top, (low = top->lo), (high = top->hi)))
#define	STACK_NOT_EMPTY	(stack < top)
void qsort_glibc(void *const pbase, size_t total_elems, size_t size, COMPAR cmp)
{
    register char *base_ptr = (char *)pbase;

    const size_t max_thresh = MAX_THRESH * size;

    if (total_elems == 0)
        /* Avoid lossage with unsigned arithmetic below.  */
        return;

    if (total_elems > MAX_THRESH)
    {
        char *lo = base_ptr;
        char *hi = &lo[size * (total_elems - 1)];
        stack_node stack[STACK_SIZE];
        stack_node *top = stack;

        PUSH(NULL, NULL);

        while (STACK_NOT_EMPTY)
        {
            char *left_ptr;
            char *right_ptr;

            /* Select median value from among LO, MID, and HI. Rearrange
            LO and HI so the three values are sorted. This lowers the
            probability of picking a pathological pivot value and
            skips a comparison for both the LEFT_PTR and RIGHT_PTR in
            the while loops. */

            char *mid = lo + size * ((hi - lo) / size >> 1);

            if ((*cmp) ((void *)mid, (void *)lo) < 0)
                SWAP(mid, lo, size);
            if ((*cmp) ((void *)hi, (void *)mid) < 0)
                SWAP(mid, hi, size);
            else
                goto jump_over;
            if ((*cmp) ((void *)mid, (void *)lo) < 0)
                SWAP(mid, lo, size);
        jump_over:;

            left_ptr = lo + size;
            right_ptr = hi - size;

            /* Here's the famous ``collapse the walls'' section of quicksort.
            Gotta like those tight inner loops!  They are the main reason
            that this algorithm runs much faster than others. */
            do
            {
                while ((*cmp) ((void *)left_ptr, (void *)mid) < 0)
                    left_ptr += size;

                while ((*cmp) ((void *)mid, (void *)right_ptr) < 0)
                    right_ptr -= size;

                if (left_ptr < right_ptr)
                {
                    SWAP(left_ptr, right_ptr, size);
                    if (mid == left_ptr)
                        mid = right_ptr;
                    else if (mid == right_ptr)
                        mid = left_ptr;
                    left_ptr += size;
                    right_ptr -= size;
                }
                else if (left_ptr == right_ptr)
                {
                    left_ptr += size;
                    right_ptr -= size;
                    break;
                }
            } while (left_ptr <= right_ptr);

            /* Set up pointers for next iteration.  First determine whether
            left and right partitions are below the threshold size.  If so,
            ignore one or both.  Otherwise, push the larger partition's
            bounds on the stack and continue sorting the smaller one. */

            if ((size_t)(right_ptr - lo) <= max_thresh)
            {
                if ((size_t)(hi - left_ptr) <= max_thresh)
                    /* Ignore both small partitions. */
                    POP(lo, hi);
                else
                    /* Ignore small left partition. */
                    lo = left_ptr;
            }
            else if ((size_t)(hi - left_ptr) <= max_thresh)
                /* Ignore small right partition. */
                hi = right_ptr;
            else if ((right_ptr - lo) > (hi - left_ptr))
            {
                /* Push larger left partition indices. */
                PUSH(lo, right_ptr);
                lo = left_ptr;
            }
            else
            {
                /* Push larger right partition indices. */
                PUSH(left_ptr, hi);
                hi = right_ptr;
            }
        }
    }

    /* Once the BASE_PTR array is partially sorted by quicksort the rest
    is completely sorted using insertion sort, since this is efficient
    for partitions below MAX_THRESH size. BASE_PTR points to the beginning
    of the array to sort, and END_PTR points at the very last element in
    the array (*not* one beyond it!). */

//#define min(x, y) ((x) < (y) ? (x) : (y))

    {
        char *const end_ptr = &base_ptr[size * (total_elems - 1)];
        char *tmp_ptr = base_ptr;
        char *thresh = min(end_ptr, base_ptr + max_thresh);
        register char *run_ptr;

        /* Find smallest element in first threshold and place it at the
        array's beginning.  This is the smallest array element,
        and the operation speeds up insertion sort's inner loop. */

        for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
            if ((*cmp) ((void *)run_ptr, (void *)tmp_ptr) < 0)
                tmp_ptr = run_ptr;

        if (tmp_ptr != base_ptr)
            SWAP(tmp_ptr, base_ptr, size);

        /* Insertion sort, running from left-hand-side up to right-hand-side.  */

        run_ptr = base_ptr + size;
        while ((run_ptr += size) <= end_ptr)
        {
            tmp_ptr = run_ptr - size;
            while ((*cmp) ((void *)run_ptr, (void *)tmp_ptr) < 0)
                tmp_ptr -= size;

            tmp_ptr += size;
            if (tmp_ptr != run_ptr)
            {
                char *trav;

                trav = run_ptr + size;
                while (--trav >= run_ptr)
                {
                    char c = *trav;
                    char *hi, *lo;

                    for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
                        *hi = *lo;
                    *hi = c;
                }
            }
        }
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值