## 介绍 排序(Sorting) 是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个关键字有序的序列。排序就是把集合中的元素按照一定的次序排序在一起,一般来说有升序排列和降序排列2种排序。 排序算法的逻辑是固定的,所要排序的数据集结构和数据项结构会不同,当针对每种数据集结构或者数据项结构都写一套排序算法函数,那无疑会增加冗余的代码。 varch提供sort模块就为了解决这个问题,把算法模型所需的数据集和数据项类型的不同进行抽象化,只要针对不同数据以及排序规则提供对应的操作函数,就可以统一调用排序算法进行排序操作。 ## 接口 ```c int sort_bubble(void *array, int begin, int end, SOPS* ops); int sort_select(void *array, int begin, int end, SOPS* ops); int sort_insert(void *array, int begin, int end, SOPS* ops); int sort_shell(void *array, int begin, int end, SOPS* ops); int sort_quick(void *array, int begin, int end, SOPS* ops); int sort_heap(void *array, int begin, int end, SOPS* ops); ``` 这里给出了6种常用的排序法,分别是冒泡排序、选择排序、插入排序、希尔排序、快速排序、堆排序,这几个排序法在使用上一致。 `array`传入数组或者其他任意的数据容器,`begin`和`end`指定需要排序的区间,`ops`则是指定排序所需的一些函数操作集。 ```c /* Sorting algorithm operation function set structure definition */ typedef struct { /** * \brief ordering rules, e.g front < back ascending and front > back descending * \param[in] front: address of front data * \param[in] back: address of back data * \return positive: following sorting rules * negative: violating sorting rules * 0: does't affect sorting rules */ int (*order)(void *front, void *back); /** * \brief get the address of the space where the element is located * \param[in] array: data handle * \param[in] index: item data index * \return address of item data */ void* (*addr)(void *array, int index); /** * \brief Swap data between two spaces * \param[in] array: data handle * \param[in] index0: item data index0 * \param[in] index1: item data index1 * \return none */ void (*swap)(void *array, int index0, int index1); } SOPS; ``` 首先需要在`ops`中`order`指定排序的规则(升序还是降序),`front`的项大于`back`的项,那就是降序的规则,反之亦然。 因为`array`支持任意类型的数据结构,所以要在`ops`中`addr`成员指定获取特定数据结构项的指定成员地址,比如`char`数组和`int`数组返回相应地址不一样。 排序就涉及数据的位置交换,`ops`的`swap`成员就指定交换数据的方法(因为并不是所有数据结构的交换数据都是直接交换亮着数据的内容,比如链表就是交换数据指向就可以了)。 常用的数据结构排序的`ops`已经封装好了,根据类型选择实际`ops`即可。 ```c extern SOPS sops_char_ascend; extern SOPS sops_char_descend; extern SOPS sops_uchar_ascend; extern SOPS sops_uchar_descend; extern SOPS sops_short_ascend; extern SOPS sops_short_descend; extern SOPS sops_ushort_ascend; extern SOPS sops_ushort_descend; extern SOPS sops_int_ascend; extern SOPS sops_int_descend; extern SOPS sops_uint_ascend; extern SOPS sops_uint_descend; extern SOPS sops_float_ascend; extern SOPS sops_float_descend; extern SOPS sops_double_ascend; extern SOPS sops_double_descend; ``` ## 实测例子 ### 基本升降序用法 针对基本数据类型的升降序排序,以以冒泡排序算法举例。 ```c static void test_basic_usage(void) { /* 针对int型数组,进行升序 */ int array_int[] = {1, 3, 6, 5, 0, 2, 9, 8, 7, -4}; // 定义时候任意乱序 // 传入数组基地址,排序的区间,指定int型的升序ops sort_bubble(array_int, 0, sizeof(array_int)/sizeof(array_int[0]) - 1, &sops_int_ascend); // 输出排序后的数组 for (int i = 0; i < sizeof(array_int)/sizeof(array_int[0]); i++) { printf("%d, ", array_int[i]); } printf("\r\n"); /* ---------------------------------------- 代码分割线 ---------------------------------------- */ /* 针对float型数组,进行降序 */ float array_float[] = {-1.1, 2.1, 6.6, 5.0, 0.1, 2.8, 9.9, 9.8, 7.0, -4.5}; // 定义时候任意乱序 // 传入数组基地址,排序的区间,指定float型的降序ops sort_bubble(array_float, 0, sizeof(array_float)/sizeof(array_float[0]) - 1, &sops_float_descend); // 输出排序后的数组 for (int i = 0; i < sizeof(array_float)/sizeof(array_float[0]); i++) { printf("%.2f, ", array_float[i]); } printf("\r\n"); } ``` 测试结果: ``` -4, 0, 1, 2, 3, 5, 6, 7, 8, 9, 9.90, 9.80, 7.00, 6.60, 5.00, 2.80, 2.10, 0.10, -1.10, -4.50, ``` ### 针对指定区间进行排序 除了一般常用的全区间进行排序,还可以指定某段区间内进行。 ```c static void test_interval(void) { int array_int[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // 先定义升序的数组 // 传入数组基地址,指定在[3, 7]区间内进行排序,指定int型的降序ops sort_bubble(array_int, 3, 7, &sops_int_descend); // 输出排序后的数组 for (int i = 0; i < sizeof(array_int)/sizeof(array_int[0]); i++) { printf("%d, ", array_int[i]); } printf("\r\n"); } ``` 测试结果: ``` 0, 1, 2, 7, 6, 5, 4, 3, 8, 9, ``` ### 针对不是数组的其他容器排序 下面以list容器为例子,测试int型list升序排序。 这个测试代码依赖`list`容器。 ```c // 获取list数据项地址 static void* list_int_addr(void *array, int index) { return list_data((list_t)array, index); } // 交换list两个成员数据 static void list_int_swap(void *array, int index0, int index1) { int temp = list_at((list_t)array, int, index0); list_at((list_t)array, int, index0) = list_at((list_t)array, int, index1); list_at((list_t)array, int, index1) = temp; } // 显示list存储的数据 static void list_int_show(list_t list) { for (int i = 0; i < list_size(list); i++) { printf("%d, ", list_at(list, int, i)); } printf("\r\n"); } static void test_list_sort(void) { list_t list = list(int); // 定义一个int型的list SOPS ops; // 定义一个新的ops int array[] = {1, 3, 6, 5, 0, 2, 9, 8, 7, -4}; // 定义要给list装载的数据 // 将数据装载到list for (int i = 0; i < sizeof(array)/sizeof(array[0]); i++) { list_push_back(list, &array[i]); } ops.order = sops_int_ascend.order; // 复用int升序规则 ops.addr = list_int_addr; // 使用list获取元素的地址 ops.swap = list_int_swap; // list交换数据 // 输出排序前的list list_int_show(list); // 进行排序 sort_bubble(list, 0, list_size(list) - 1, &ops); // 输出排序后的list list_int_show(list); // 释放list _list(list); } ``` 测试结果: ``` 1, 3, 6, 5, 0, 2, 9, 8, 7, -4, -4, 0, 1, 2, 3, 5, 6, 7, 8, 9, ``` ### 针对自定义数据结构进行排序 前面提供的例子都是基础数据类型的,接下来的例子是针对自定义数据类型的,比如结构体,根据结构体的某些成员项进行排序。 ```c typedef struct { char *name; int age; char gender; /* 1 表示男,0 表示女 */ float weight; } STUDENT; static void student_print(STUDENT *s, int size) { for (int i = 0; i < size; i++) { printf("name: %s, \t\t age = %d, \t\t gender = %d, \t\t weight = %.2f\r\n", s[i].name, s[i].age, s[i].gender, s[i].weight); } } static void* student_addr(void *array, int index) { return ((STUDENT *)array) + index; } static void student_swap(void *array, int index0, int index1) { STUDENT temp = ((STUDENT *)array)[index0]; ((STUDENT *)array)[index0] = ((STUDENT *)array)[index1]; ((STUDENT *)array)[index1] = temp; } static int student_name_ascend(void *front, void *back) { return -strcmp(((STUDENT *)front)->name, ((STUDENT *)back)->name); } static int student_gender_descend(void *front, void *back) { if (((STUDENT *)front)->gender > ((STUDENT *)back)->gender) return 1; else if (((STUDENT *)front)->gender < ((STUDENT *)back)->gender) return -1; else return 0; } static int student_age_descend(void *front, void *back) { if (((STUDENT *)front)->age > ((STUDENT *)back)->age) return 1; else if (((STUDENT *)front)->age < ((STUDENT *)back)->age) return -1; else return 0; } static int student_weight_ascend(void *front, void *back) { if (((STUDENT *)front)->weight < ((STUDENT *)back)->weight) return 1; else if (((STUDENT *)front)->weight > ((STUDENT *)back)->weight) return -1; else return 0; } static void test_struct(void) { SOPS ops; STUDENT table[] = { {"ZhanSan", 18, 1, 56.3}, {"LiSi", 17, 1, 62.2}, {"WangWu", 21, 0, 58.9}, {"ZhaoLiu", 22, 1, 65.5}, {"SunQi", 18, 1, 71.7}, {"ZhouBa", 30, 0, 48.8}, {"WuJiu", 16, 1, 56.3}, {"ZhenShi", 19, 0, 52.1}, }; ops.addr = student_addr; ops.swap = student_swap; // 指定为按照名字升序排序 ops.order = student_name_ascend; sort_bubble(table, 0, sizeof(table)/sizeof(table[0])-1, &ops); printf("sorting by name in ascending order\r\n"); student_print(table, sizeof(table)/sizeof(table[0])); printf("---------------------------------\r\n"); // 指定为按照年龄降序排序 ops.order = student_age_descend; sort_bubble(table, 0, sizeof(table)/sizeof(table[0])-1, &ops); printf("sorting by age in descending order\r\n"); student_print(table, sizeof(table)/sizeof(table[0])); printf("---------------------------------\r\n"); // 指定为按照性别降序排序 ops.order = student_gender_descend; sort_bubble(table, 0, sizeof(table)/sizeof(table[0])-1, &ops); printf("sorting by gender in descending order\r\n"); student_print(table, sizeof(table)/sizeof(table[0])); printf("---------------------------------\r\n"); // 指定为按照体重升序排序 ops.order = student_weight_ascend; sort_bubble(table, 0, sizeof(table)/sizeof(table[0])-1, &ops); printf("sorting by weight in ascending order\r\n"); student_print(table, sizeof(table)/sizeof(table[0])); printf("---------------------------------\r\n"); } ``` 测试结果: ``` sorting by name in ascending order name: LiSi, age = 17, gender = 1, weight = 62.20 name: SunQi, age = 18, gender = 1, weight = 71.70 name: WangWu, age = 21, gender = 0, weight = 58.90 name: WuJiu, age = 16, gender = 1, weight = 56.30 name: ZhanSan, age = 18, gender = 1, weight = 56.30 name: ZhaoLiu, age = 22, gender = 1, weight = 65.50 name: ZhenShi, age = 19, gender = 0, weight = 52.10 name: ZhouBa, age = 30, gender = 0, weight = 48.80 --------------------------------- sorting by age in descending order name: ZhouBa, age = 30, gender = 0, weight = 48.80 name: ZhaoLiu, age = 22, gender = 1, weight = 65.50 name: WangWu, age = 21, gender = 0, weight = 58.90 name: ZhenShi, age = 19, gender = 0, weight = 52.10 name: ZhanSan, age = 18, gender = 1, weight = 56.30 name: SunQi, age = 18, gender = 1, weight = 71.70 name: LiSi, age = 17, gender = 1, weight = 62.20 name: WuJiu, age = 16, gender = 1, weight = 56.30 --------------------------------- sorting by gender in descending order name: ZhaoLiu, age = 22, gender = 1, weight = 65.50 name: ZhanSan, age = 18, gender = 1, weight = 56.30 name: SunQi, age = 18, gender = 1, weight = 71.70 name: LiSi, age = 17, gender = 1, weight = 62.20 name: WuJiu, age = 16, gender = 1, weight = 56.30 name: WangWu, age = 21, gender = 0, weight = 58.90 name: ZhenShi, age = 19, gender = 0, weight = 52.10 name: ZhouBa, age = 30, gender = 0, weight = 48.80 --------------------------------- sorting by weight in ascending order name: ZhouBa, age = 30, gender = 0, weight = 48.80 name: ZhenShi, age = 19, gender = 0, weight = 52.10 name: ZhanSan, age = 18, gender = 1, weight = 56.30 name: WuJiu, age = 16, gender = 1, weight = 56.30 name: WangWu, age = 21, gender = 0, weight = 58.90 name: LiSi, age = 17, gender = 1, weight = 62.20 name: ZhaoLiu, age = 22, gender = 1, weight = 65.50 name: SunQi, age = 18, gender = 1, weight = 71.70 --------------------------------- ```