varch/doc/sort.md
2024-07-21 19:02:13 +08:00

13 KiB
Raw Blame History

介绍

排序(Sorting) 是计算机程序设计中的一种重要操作它的功能是将一个数据元素或记录的任意序列重新排列成一个关键字有序的序列。排序就是把集合中的元素按照一定的次序排序在一起一般来说有升序排列和降序排列2种排序。

排序算法的逻辑是固定的,所要排序的数据集结构和数据项结构会不同,当针对每种数据集结构或者数据项结构都写一套排序算法函数,那无疑会增加冗余的代码。

varch提供sort模块就为了解决这个问题把算法模型所需的数据集和数据项类型的不同进行抽象化只要针对不同数据以及排序规则提供对应的操作函数就可以统一调用排序算法进行排序操作。

接口

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传入数组或者其他任意的数据容器,beginend指定需要排序的区间,ops则是指定排序所需的一些函数操作集。

/* 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;

首先需要在opsorder指定排序的规则(升序还是降序),front的项大于back的项,那就是降序的规则,反之亦然。 因为array支持任意类型的数据结构,所以要在opsaddr成员指定获取特定数据结构项的指定成员地址,比如char数组和int数组返回相应地址不一样。 排序就涉及数据的位置交换,opsswap成员就指定交换数据的方法(因为并不是所有数据结构的交换数据都是直接交换亮着数据的内容,比如链表就是交换数据指向就可以了)。

常用的数据结构排序的ops已经封装好了,根据类型选择实际ops即可。

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;

实测例子

基本升降序用法

针对基本数据类型的升降序排序,以以冒泡排序算法举例。

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,

针对指定区间进行排序

除了一般常用的全区间进行排序,还可以指定某段区间内进行。

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容器。

// 获取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,

针对自定义数据结构进行排序

前面提供的例子都是基础数据类型的,接下来的例子是针对自定义数据类型的,比如结构体,根据结构体的某些成员项进行排序。

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
---------------------------------