资讯专栏INFORMATION COLUMN

怎么样才能做到对多种数据类型排序?C语言快速排序——qsort函数及其模拟实现

alphahans / 1121人阅读

摘要:我们以冒泡排序为例,模拟实现函数。交换每单位字节对于的二进制序列这样,冒泡排序就能排序多种数据类型,模拟实现了函数,当然也可以使用其他的排序方法模拟实现函数。

⭐️前面的话⭐️

大家好!对于排序有许多中方法,比如冒泡排序,选择排序,希尔排序,插入排序,堆排序等等,但是怎样能够使用一个函数能够对多个数据类型进行排序呢?无所不知的C语言开发者提供了一个qsort函数,它能够对多种数据类型进行排序,实现各种数据类型的快速排序,这篇文章介绍qsort函数的使用及其模拟qsort函数的实现(基于冒泡排序)。

?博客主页:未见花闻的博客主页
?欢迎关注?点赞?收藏⭐️留言?
?本文由未见花闻原创,CSDN首发!
?首发时间:?2021年9月7日?
✉️坚持和努力一定能换来诗与远方!
?参考书籍:?《明解C语言》?《C primer plus》
?参考在线编程网站:?牛客网?力扣
?作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!
博主的码云gitee,平常博主写的程序代码都在里面。


⭐️qsort库函数

?函数声明

void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );

?库

?函数功能

实现多个数据类型的快速排序。

?qosort函数各参数介绍

  • 返回类型:void 无返回值
  • 参数base:void* 无类型指针,可以接受任何指针类型,但是不能进行解引用,不能进行运算。该参数用来接受需要排序数组的首地址。
  • 参数num:size_t类型,本质上为无符号整型类型,该参数接受数组中元素的数量。
  • 参数width:size_t类型,本质上无符号整型类型,该参数接受数组中元素的内存大小,单位为字节。
  • 参数compare:函数指针类型,指向的函数类型为返回值为int,拥有2个参数的函数,其实这两个参数类型都为const void指针。该函数用来比较两个地址对应的元素的大小。该参数用来回调compare所指向的函数,是实现多数据类型排序的核心。

?compare函数

qsort函数中,其中一个参数是函数指针,指向一个用来比较两个元素大小的函数,不妨记该函数的第一个参数为e1,第二个参数为e2,两个参数均为只可读无类型指针,该函数返回值类型为int,记为x

  • x>0,表示e1指向的元素大于e2指向的元素。
  • x=0,表示e1指向的元素等于e2指向的元素。
  • x<0,表示e1指向的元素小于e2指向的元素。

实现整型类型比较的compare函数
如果qsort需要实现实现升序:

//升序int compare(const void* a, const void* b){	return (*(int*)a - *(int*)b);}

那需要实现降序呢?
其实很简单,将上面return (*(int*)a - *(int*)b);改为return (*(int*)b - *(int*)a);就可以了。
实现浮点数比较的compare函数

int compare_dou(const void* a, const void* b){	double com = (*(double*)a - *(double*)b);	//防止数据发生截断造成排序结果错误	if (com > 0)		return 1;	else if (com < 0)		return -1;	else		return 0;}int compare_fla(const void* a, const void* b){	float com = (*(float*)a - *(float*)b);	//防止数据发生截断造成排序结果错误	if (com > 0)		return 1;	else if (com < 0)		return -1;	else		return 0;}

实现字符类型和字符串比较的compare函数

int compare(const void* a, const void* b){	return (*(char*)a - *(char*)b);}

实现结构体类型比较compare函数

//参考结构体struct stu{	char name[20];	int age;	double score;};
int compare_stu(const void* a, const void* b){	//如果是字符串	return strcmp(((struct stu*)a)->name, ((struct stu*)b)->name);	//如果整型	//return (((struct stu*)a)->age - ((struct stu*)b)->age);	//如果是浮点型	//float com = (*(float*)a - *(float*)b);	//防止数据发生截断造成排序结果错误	//if (com > 0)	//	return 1;	/*else if (com < 0)		return -1;	else		return 0;*/}

?测试

测试主函数

#include #include #include int main (){//test;}

测试1:整数排序

void test1(){	int arr[] = { 2,8,6,12,3,86,1,42,66,22,98,88 };	sz_t number = sizeof(arr) / sizeof(arr[0]);	sz_t size = sizeof(int);	sz_t i = 0;	printf("数组排序前:/n");	for (i = 0; i < number; i++)	{		printf("%d ", arr[i]);	}	qsort(arr, number, size, compare);	printf("/n数组排序后:/n");	for (i = 0; i < number; i++)	{		printf("%d ", arr[i]);	}}
数组排序前:2 8 6 12 3 86 1 42 66 22 98 88数组排序后:1 2 3 6 8 12 22 42 66 86 88 98D:/gtee/C-learning-code-and-project/练习使用qsort/Debug/练习使用qsort.exe (进程 37064)已退出,代码为 0。按任意键关闭此窗口. . .

测试2:双精度浮点数排序

void test2(){	double arr[5] = { 3.5,8.9,9.2,4.8,2.1 };	sz_t number = sizeof(arr) / sizeof(arr[0]);	sz_t size = sizeof(double);	sz_t i = 0;	printf("数组排序前:/n");	for (i = 0; i < number; i++)	{		printf("%.2lf ", arr[i]);	}	qsort(arr, number, size, compare_dou);	printf("/n数组排序后:/n");	for (i = 0; i < number; i++)	{		printf("%.2lf ", arr[i]);	}}
数组排序前:3.50 8.90 9.20 4.80 2.10数组排序后:2.10 3.50 4.80 8.90 9.20D:/gtee/C-learning-code-and-project/练习使用qsort/Debug/练习使用qsort.exe (进程 9984)已退出,代码为 0。按任意键关闭此窗口. . .

测试3:结构体中字符串排序测试

void test3(){	struct stu arr[3] = { {"zhangsan", 15}, {"lisi", 30},{"wangwu", 10} };	sz_t number = sizeof(arr) / sizeof(arr[0]);	sz_t size = sizeof(arr[0]);	sz_t i = 0;	printf("数组排序前:/n");	for (i = 0; i < number; i++)	{		printf("%s ", arr[i].name);	}	qsort(arr, number, size, compare_stu);	printf("/n数组排序后:/n");	for (i = 0; i < number; i++)	{		printf("%s ", arr[i].name);	}}
数组排序前:zhangsan lisi wangwu数组排序后:lisi wangwu zhangsanD:/gtee/C-learning-code-and-project/练习使用qsort/Debug/练习使用qsort.exe (进程 29572)已退出,代码为 0。按任意键关闭此窗口. . .

⭐️模拟实现qsort函数

经过对qsort函数的了解,我们发现该函数能够对多种数据类型进行排序取决于传入的函数指针参数。我们以冒泡排序为例,模拟实现qsort函数。

?冒泡排序改装思路

先来看看冒泡排序函数

#define _CRT_SECURE_NO_WARNINGS 1#include //冒泡排序(int) 从小到大void bubble_sort(int* arr, int size){	int i = 0;	for (i = 0; i < size - 1; i++)	{		int j = 0;		int flag = 1;//判断数据是否发生交换,默认没有发生交换		for (j = 0; j < size - 1 - i; j++)		{			if (arr[j] > arr[j + 1])			{				int tmp = arr[j];				arr[j] = arr[j + 1];				arr[j + 1] = tmp;				flag = 0;			}		}		if (flag)			break;	}}

如果需要对多种数据类型进行排序,对应上面的这个冒泡排序,它所接受的参数肯定是必须改的,因为要接受多种数据类型的指针,所以传入的指针必须是无具体类型void。函数内部中循环排序的次数是没有发生改变的,所以函数内部的两层循环是不用发生改变的,但是由于传进来的是void型指针,无法进行运算和解引用,所以判断语句是需要进行改动的。

?冒泡排序模拟实现qsort函数

对与if语句中所对应条件,我们可以调用compare函数,将数组的前一个元素与后一个元素比较,如果该函数返回值大于0,表示数组前面的元素比后面的元素大,如果进行升序排列,我们需要对这两个元素进行交换。这个交换我们不妨封装在一个swap函数里,由于不知道数据类型,所以swap函数的参数为两个元素的无类型地址,交换的时候我们不妨强制转换为char*类型,因为char类型大小为1字节,根据需要交换元素的大小,我们一个一个地将每个单位字节的二进制序列交换,这样就完成了交换。对于如何得到相邻元素的首地址,同理我们先可以将传入指针强制转换为char*类型任何根据元素内存大小,计算的出每个元素的地址。比如数组元素内存大小为width,则相邻两元素地址可以表示为(char*)val + j * width, (char*)val + (j + 1) * width
知道了冒泡排序哪个地方需要改装,我们来试着动手实践一下。

typedef unsigned int sz_t;
void bubble_qsort(void* val, sz_t size, sz_t width, int (*cmp)(const void* p1, const void* p2)){	sz_t i = 0;	for (i = 0; i < size - 1; i++)	{		sz_t j = 0;		sz_t flag = 1;		for (j = 0; j < size - 1 - i; j++)		{			if (cmp((char*)val + j * width, (char*)val + (j + 1) * width) > 0)			{				swap((char*)val + j * width, (char*)val + (j + 1) * width, width);				flag = 0;			}		}		if (flag)			break;	}}
void swap(char* buf1, char* buf2, sz_t width){	sz_t i = 0;	for (i = 0; i < width; i++)	{		char tmp = *(buf1 + i);		*(buf1 + i) = *(buf2 + i);		*(buf2 + i) = tmp;//交换每单位字节对于的二进制序列	}}

这样,冒泡排序就能排序多种数据类型,模拟实现了qsort函数,当然也可以使用其他的排序方法模拟实现qsort函数。

?测试

测试主函数

#include #include #include int main (){//test;}

测试1:整数排序

void test1(){	int arr[] = { 2,8,6,12,3,86,1,42,66,22,98,88 };	sz_t number = sizeof(arr) / sizeof(arr[0]);	sz_t size = sizeof(int);	sz_t i = 0;	printf("数组排序前:/n");	for (i = 0; i < number; i++)	{		printf("%d ", arr[i]);	}	bubble_qsort(arr, number, size, compare);	printf("/n数组排序后:/n");	for (i = 0; i < number; i++)	{		printf("%d ", arr[i]);	}}
数组排序前:2 8 6 12 3 86 1 42 66 22 98 88数组排序后:1 2 3 6 8 12 22 42 66 86 88 98D:/gtee/C-learning-code-and-project/练习使用qsort/Debug/练习使用qsort.exe (进程 10620)已退出,代码为 0。按任意键关闭此窗口. . .

测试2:双精度浮点数排序

void test2(){	double arr[5] = { 3.5,8.9,9.2,4.8,2.1 };	sz_t number = sizeof(arr) / sizeof(arr[0]);	sz_t size = sizeof(double);	sz_t i = 0;	printf("数组排序前:/n");	for (i = 0; i < number; i++)	{		printf("%.2lf ", arr[i]);	}	bubble_qsort(arr, number, size, compare_dou);	printf("/n数组排序后:/n");	for (i = 0; i < number; i++)	{		printf("%.2lf ", arr[i]);	}}
数组排序前:3.50 8.90 9.20 4.80 2.10数组排序后:2.10 3.50 4.80 8.90 9.20D:/gtee/C-learning-code-and-project/练习使用qsort/Debug/练习使用qsort.exe (进程 34200)已退出,代码为 0。按任意键关闭此窗口. . .

测试3:结构体中字符串排序测试

void test3(){	struct stu arr[3] = { {"zhangsan", 15}, {"lisi", 30},{"wangwu", 10} };	sz_t number = sizeof(arr) / sizeof(arr[0]);	sz_t size = sizeof(arr[0]
                 
               
              

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/119676.html

相关文章

  • C语言进阶:指针进阶续

    摘要:故使用无具体类型,又称通用类型,即可以接收任意类型的指针,但是无法进行指针运算解引用,整数等。求指针所占字节而不是解引用访问权限大小。数组就是整个数组的大小,数组元素则是数组元素的大小,指针大小都为。 ...

    ingood 评论0 收藏0
  • C语言-qsort函数详解

    摘要:目录一函数是什么二使用排序以升序为例关于型指针整形数组排序字符数组排序字符指针数组排序结构体数组排序浮点型数组排序三使用冒泡排序思想模拟实现函数什么是冒泡排序冒泡排序代码使用冒泡排序思想模 目录 一.qsort函数是什么  二.使用qsort排序-以升序为例       关于void*型指针...

    Airmusic 评论0 收藏0
  • C语言qsort()函数的使用(详解)

    摘要:参数含义上图是函数各个参数的含义,让我们一个个来看。使用方式头文件要使用函数我们首先需要引用一个头文件的实现函数给函数规定了特定的参数。因此我们设计函数时要严格遵守其参数设定。 目录 1.参数含义 1.首元素地址base 2.元素个数num 3.元素大小size 4.自定义比较函数compa...

    wangym 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<