C语言qsort()函数及其模拟实现

在日常生活中,经常涉及到对数据的处理和分析。有时我们需要对一组杂乱无章的数据进行排序,这时候就需要用到排序算法。一般的对于整型数据的处理,我们可以用冒泡排序:

冒泡排序的应用举例如下:

#include
#include
void test0(int arr[], int sz)//冒泡排序基本算法
{
	int i, j;
	//进行冒泡排序的趟数
	for (i = 0; i < sz; ++i)
	{   //每趟冒泡排序的对数
		for (j = 0; j < sz - i - 1; ++j)
		{
			if (arr[j] > arr[j + 1])
			{  //交换
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	int arr[] = { 5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	
	printf("before :  ");
	for (int i = 0; i < sz; ++i)
	{
		printf("%d ", arr[i]);
	}
	test0(arr, sz);
	printf("\n");
	printf("after: ");
	for (int i = 0; i < sz; ++i)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

输出结果如下;

C语言qsort()函数及其模拟实现_第1张图片

 然而生活中我们碰到的数据远不止是整型,有时是姓名,浮点小数(利率)以及甚至是一个复杂的对象,因此上述这种冒泡排序的弊端就显现出来了(只能排序整数)。事实上,C语言提供了一个为我们排序复杂对象的函数-qsort()。

我们先来看qsort()的函数原型:

void qsort (void* base, size_t num, size_t size,
            int (*compar)(const void*,const void*));

其中size_t是一个无符号整型的别名,compar是一个函数指针(存放函数地址)

下面我们来使用qsort()来排序浮点数:

#include
#include//包含qsort()的原型及定义
//参数是函数指针,说明该指针能够接受形参类型相同,返回类型也相同的函数
int cmp_float(const float * a,const float* b)//比较浮点数的函数(使得能被收)
{
	if (*a == *b)
		return 0;
	else if (*a > *b)
		return 1;
	else
		return -1;
}
int main()
{
	float arr[] = { 5.0,4.0,3.0,2.0,1.0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("before: ");
	for (int i = 0; i < sz; ++i)
	{
		printf("%lf    ", arr[i]);
	}
	qsort(arr,sz, sizeof(arr[0]), cmp_float);
	printf("\n");
	printf("after: ");
	for (int i = 0; i < sz; ++i)
	{
		printf("%lf   ", arr[i]);
	}
	return 0;
}

运行结果如下:

C语言qsort()函数及其模拟实现_第2张图片

 讲完了怎么使用,不妨我们尝试模拟实现这一个函数(核心算法是冒泡排序)

我们注意到qsort()函数的第一个参数的类型是一个无类型的指针(不是空指针)(意味着我们可以用强制类型转换成我们所要的指针),同时我们还要知道元素的个数和元素的大小,最重要的是我们要针对不同的数据类型设计不同的比较方法(把比较方法抽离出来) 

核心部分的代码:

void Myqsort(void* base, int sz, int width, int(*cmp)(void* e1, void* e2))
{
	int i = 0
		;
	//比较趟数
	for (i = 0; i < sz; ++i)
	{  //每趟比较对数
		for (int j = 0; j < sz - i - 1; ++j)
		{//强制成字符指针,是因为字符型指针每解引用一次都会访问一个字节。	
			//乘以宽度就是跳过宽度个字节,再乘以变量j就能控制访问每一对元素
			{    if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}

}

交换部分:

void swap(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; ++i)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		++buf1;
		++buf2;
	}
}

 抽离的比较方法:

//比较整数
int cmp_int(void* e1, void* e2)
{    
	return *(int*)(e1)-*(int*)(e2);
}
//比较浮点数
int cmp_float(void* e1, void* e2)
{
	if (*(float*)e1 == *(float*)e2) return 0;
	else if (*(float*)e1 > *(float*)e2) return 1;
	else return -1;
}
//比较学生结构体的年龄
int cmp_stu_age(void* e1, void* e2)
{
	return ((Stu*)e1)->age - ((Stu*)e2)->age;
}
//比较学生结构体的姓名
int cmp_stu_name(void* e1, void* e2)
{ //箭头的优先级比较高,所以要把强转的结果括号起来
	return strcmp(((Stu*)e1)->name, ((Stu*)e2)->name);
}

 测试的部分:

//测试整型
	void testi()
	{
			int arr[] = { 9,8,7,6,5,4,3,2,1 };
			int sz = sizeof(arr) / sizeof(arr[0]);
			printf("before:");
			for (int i = 0; i < sz; ++i)
			{
				printf("%d ", arr[i]);
			}
			printf("\n");
			printf("after:  ");
			Myqsort(arr, sz, sizeof(arr[0]), cmp_int);
			for (int i = 0; i < sz; ++i)
			{
				printf("%d ", arr[i]);
			}
	}
	//测试浮点型
	void testf()
	{
		float arr[] = { 9.0,8.0,7.0,6.0,5.0,4.0,3.0,2.0,1.0 };
		int sz = sizeof(arr) / sizeof(arr[0]);
		printf("before:");
		for (int i = 0; i < sz; ++i)
		{
			printf("%f  ", arr[i]);
		}
		printf("\n");
		printf("after:  ");
		Myqsort(arr, sz, sizeof(arr[0]), cmp_int);
		for (int i = 0; i < sz; ++i)
		{
			printf("%f  ", arr[i]);
		}
	}
	//测试年龄
	void testa()
	{
		Stu s[] = { {"zhansan",16},{"lisi",28}, {"wangwu",22} };
		int sz = sizeof(s) / sizeof(s[0]);
		printf("before:");
		for (int i = 0; i < sz; ++i)
		{
			printf("%s", s[i].name);
			printf("%d ", s[i].age);
		}
		printf("\n");
		printf("after:  ");
		Myqsort(s, sz, sizeof(s[0]), cmp_stu_age);
		for (int i = 0; i < sz; ++i)
		{
			printf("%s", s[i].name);
			printf("%d  ", s[i].age);
		}
	}
	//测试姓名
	void testn()
	{
		Stu s[] = { {"zhangsan",16},{"lisi",43}, {"wangwu",22} };
		int sz = sizeof(s) / sizeof(s[0]);
		printf("before:");
		for (int i = 0; i < sz; ++i)
		{
			printf("%s", s[i].name);
			printf("%d ", s[i].age);
		}
		printf("\n");
		printf("after:  ");
		Myqsort(s, sz, sizeof(s[0]), cmp_stu_name);
		for (int i = 0; i < sz; ++i)
		{
			printf("%s", s[i].name);
			printf("%d  ", s[i].age);
		}
	}

主函数部分:

int main()
	{
		testi();
		testf();
		testa();
		testn();
		return 0;
}

测试结果如下:

C语言qsort()函数及其模拟实现_第3张图片

 C语言qsort()函数及其模拟实现_第4张图片

 比较年龄部分:

C语言qsort()函数及其模拟实现_第5张图片

比较姓名:

C语言qsort()函数及其模拟实现_第6张图片

完整代码如下:

#include
#include
//参数是函数指针,说明该指针能够接受形参类型相同,返回类型也相同的函数
typedef struct student
{
	char name[50];
	int age;
}Stu;
void swap(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; ++i)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		++buf1;
		++buf2;
	}
}
void Myqsort(void* base, int sz, int width, int(*cmp)(void* e1, void* e2))
{
	int i = 0
		;
	//比较趟数
	for (i = 0; i < sz; ++i)
	{  //每趟比较对数
		for (int j = 0; j < sz - i - 1; ++j)
		{//强制成字符指针,是因为字符型指针每解引用一次都会访问一个字节。	
			//乘以宽度就是跳过宽度个字节,再乘以变量j就能控制访问每一对元素
			{    if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}

}
//比较整数
int cmp_int(void* e1, void* e2)
{    
	return *(int*)(e1)-*(int*)(e2);
}
//比较浮点数
int cmp_float(void* e1, void* e2)
{
	if (*(float*)e1 == *(float*)e2) return 0;
	else if (*(float*)e1 > *(float*)e2) return 1;
	else return -1;
}
//比较学生结构体的年龄
int cmp_stu_age(void* e1, void* e2)
{
	return ((Stu*)e1)->age - ((Stu*)e2)->age;
}
//比较学生结构体的姓名
int cmp_stu_name(void* e1, void* e2)
{ //箭头的优先级比较高,所以要把强转的结果括号起来
	return strcmp(((Stu*)e1)->name, ((Stu*)e2)->name);
}
//测试整型
	void testi()
	{
			int arr[] = { 9,8,7,6,5,4,3,2,1 };
			int sz = sizeof(arr) / sizeof(arr[0]);
			printf("before:");
			for (int i = 0; i < sz; ++i)
			{
				printf("%d ", arr[i]);
			}
			printf("\n");
			printf("after:  ");
			Myqsort(arr, sz, sizeof(arr[0]), cmp_int);
			for (int i = 0; i < sz; ++i)
			{
				printf("%d ", arr[i]);
			}
	}
	//测试浮点型
	void testf()
	{
		float arr[] = { 9.0,8.0,7.0,6.0,5.0,4.0,3.0,2.0,1.0 };
		int sz = sizeof(arr) / sizeof(arr[0]);
		printf("before:");
		for (int i = 0; i < sz; ++i)
		{
			printf("%f  ", arr[i]);
		}
		printf("\n");
		printf("after:  ");
		Myqsort(arr, sz, sizeof(arr[0]), cmp_int);
		for (int i = 0; i < sz; ++i)
		{
			printf("%f  ", arr[i]);
		}
	}
	//测试年龄
	void testa()
	{
		Stu s[] = { {"zhansan",16},{"lisi",28}, {"wangwu",22} };
		int sz = sizeof(s) / sizeof(s[0]);
		printf("before:");
		for (int i = 0; i < sz; ++i)
		{
			printf("%s", s[i].name);
			printf("%d ", s[i].age);
		}
		printf("\n");
		printf("after:  ");
		Myqsort(s, sz, sizeof(s[0]), cmp_stu_age);
		for (int i = 0; i < sz; ++i)
		{
			printf("%s", s[i].name);
			printf("%d  ", s[i].age);
		}
	}
	//测试姓名
	void testn()
	{
		Stu s[] = { {"zhangsan",16},{"lisi",43}, {"wangwu",22} };
		int sz = sizeof(s) / sizeof(s[0]);
		printf("before:");
		for (int i = 0; i < sz; ++i)
		{
			printf("%s", s[i].name);
			printf("%d ", s[i].age);
		}
		printf("\n");
		printf("after:  ");
		Myqsort(s, sz, sizeof(s[0]), cmp_stu_name);
		for (int i = 0; i < sz; ++i)
		{
			printf("%s", s[i].name);
			printf("%d  ", s[i].age);
		}
	}
	int main()
	{
		testi();
		testf();
		testa();
		testn();
		return 0;
}

有不足之处还望指正

你可能感兴趣的:(c语言,算法)