【C语言】qsort函数模拟实现

各位读者好,上篇文章我们介绍了qsort函数,知道了qsort函数的功能。现在我们用冒泡排序的算法模拟实现qsort函数,小编拙作,恳请斧正,我们开始讲解。

目录

1.冒泡排序

1.第一趟排序

2.第二趟排序

3.第三趟排序

2.qsort函数模拟实现 

1.函数名

2.函数返回类型及参数

返回类型方面:

参数方面:

3.元素之间的比较

4.元素之间的交换

3.bubble_sort函数的应用 

1.用bubble_sort函数排序整形数组(升序):

2.用bubble_sort函数排序结构体(根据结构体成员name排序): 

4.结尾 


1.冒泡排序

在实现模拟实现qsort函数之前,我们先来看看冒泡排序的算法。

假设有一个整形数组int arr[4]={8,9,7,6};我们想将他们升序排序就可以使用冒泡排序算法:

1.第一趟排序

第一个元素与第二个元素比较,如果第一个元素大于第二个元素就交换位置,否则不交换:

8<9,不交换,数组变为:8,9,7,6;

第二个元素与第三个元素比较,如果第二个元素大于第三个元素就交换位置,否则不交换:

9>7,交换,数组变为:8,7,9,6;

第三个元素与第四个元素比较,如果第三个元素大于第四个元素就交换位置,否则不交换:

9>6,交换,数组变为:8,7,6,9;

从第一趟排序我们可以看到,最大的元素9经过比较交换就想泡泡一样一步步“浮”到顶端,所以名:冒泡排序。既然进过第一趟比较最大元素9已经排到了它该到的位置,接下来的排序就不必再将9进行比较排序了。

2.第二趟排序

第一个元素与第二个元素比较,如果第一个元素大于第二个元素就交换位置,否则不交换:

8>7,交换,数组变为:7,8,6,9;

第二个元素与第三个元素比较,如果第二个元素大于第三个元素就交换位置,否则不交换:

8>.6,交换,数组变为:7,6,8,9;

同理,第二大元素8经过第二趟排序比较交换也像泡泡一样一步步“浮”到该到的位置,所以接下来的排序也不必再将8进行比较排序了。

3.第三趟排序

第一个元素与第二个元素比较,如果第一个元素大于第二个元素就交换位置,否则不交换:

7>6,交换,数组变为:6,7,8,9;

数组升序排序成功!

我们可以看到冒泡排序算法总共需要经过”元素个数-1“趟排序就可以排序成功,而每一趟排序可以减少一个所需比较的元素,所谓每增加一趟排序,比较次数减少一次。

代码如下

#include<stdio.h>
int main()
{
	int arr[] = { 8,9,7,6 };
	int count = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < 4; i++)//打印排序前数组
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	//冒泡排序
	for (i = 0; i < count-1; i++)//排序元素个数-1趟
	{
		int j = 0;
		for (j = 0; j < count-1 - i; j++)//产生每趟相应比较次数
		{
			if (arr[j] > arr[j + 1])//如果大于就交换
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
	for (i = 0; i < 4; i++)//打印排序后数组
	{
		printf("%d ", arr[i]);
	}
	return  0;
}

运行结果如下:

  

2.qsort函数模拟实现 

基于以上算法思想,我们正式开始模拟实现qsort函数。

我们知道qsort函数可以排序任意类型的数据,所以我们要将以上的冒泡排序整形的代码进行改造使其可以排序任意类型的数据。因为基于冒泡排序算法模拟实现qsort函数,所以以上代码整体框架不必改变,就是排序的趟数和每趟相应比较次数不必改变。那我们现在以此来分析需要改变的地方:

1.函数名

我们不妨将模拟实现qsort功能的函数命名为:bubble_sort;

2.函数返回类型及参数

参考qsort函数,我们可以设置为:

void bubble_sort(void* base, size_t num, size_t size, int (*com)(const void* e1, const void* e2))

返回类型方面:

我们只是需要bubble_sort函数排序任意类型的数据即可,所以返回类型可以设为void

参数方面:

第一个参数:我们可能将任意类型的数组传参给bubble_sort函数,所以我们需要一个可以接受任意类型的指针来接收数组,我们将第一个参数设置为:void*base;

第二个参数:易知冒泡排序数组需要知道元素个数,上面的冒泡排序代码就可以看出,所以我们需要将元素个数传参给bubble_sort函数,所以我们将第二个参数设置为:size_t num;

第三个参数:我们可能通过bubble_sort函数排序任意类型的数据,所以我们需要知道这些数据类型的大小(单位:字节)以便我们向后访问几个字节,所以我们需要将数据类型大小传参给bubble_sort函数,所以我们将第三个参数设置为:size_t size;

第四个参数:参考qsort函数机制,我们需要自己写一个比较函数来告诉bubble_sort函数如何比较数据,所以我们需要将这个比较函数的地址传参给bubble_sort函数,所以我们将第四个参数设置为一个函数指针:int(*com)(const void*e1,const void*e2);

3.元素之间的比较

这里我们看到整形元素可以直接用关系操作符进行比较大小,但是qsort函数可以比较任意类型的数据,我们用bubble_sort函数模拟实现qsort函数的话元素之间的比较就不能这样比较了,例如字符串的比较就需要用到strcmp函数。所以元素比较这里我们可以调用用户自己写的比较函数(也就是bubble_sort函数的第四个参数指向的那个函数)进行比较。

既然我们要调用比较函数,那我们就要根据比较函数所需的参数传参给比较函数,根据第四个参数: int (*com)(const void* e1, const void* e2)我们知道需要传两个指针给比较函数,那我们将“arr[j]”和“arr[j+1]”的指针分别传给比较函数就刚刚好。

可是问题来了:如何表示“arr[j]”和“arr[j+1]”的地址呢?我们已经知道了首元素地址(base),但是base的类型是void*类型的,所以我们不能直接用"base+j"和“base+j+1”来表示“arr[j]”和“arr[j+1]”的地址,因为“base+j”的话,“base”不知道需要跳过几个字节。

那我们该怎么解决问题呢?其实也不难。我们将“base”强转成"char*"类型,将“arr[j]”的地址表示成“(char*)base+j*size”,这样子表示就可以精准的知道“base”需跳过“j*size”个字节表示“arr[j]”的地址。同理“arr[j+1]”的地址可以表示成“(char*)base+(j+1)*size”。

小编在这里再赘言几句,为啥要将“base”强转成“char*”类型呢?因为“char”类型大小是一个字节,这样“char*”类型的地址每次“+1”的权限就是跳过一个字节。而bubble_sort函数排序的可能是任意类型的数据,这些数据的类型大小可能是任意大小,如结构体的大小就不是固定的。而1是任意自然数的倍数,只有强转成“char*”类型才能配合“j”(元素下标)和“size”(元素大小)精准跳过所需跳过字节数。

基于以上思想:元素之间的比较可以写成“if(com((char*)base+j*size,(char*)base+(j+1)*size)>0)”;

调用比较函数,将“arr[j]”和“arr[j+1]”的地址传参给比较函数,若"arr[j]"大于“arr[j+1]”,比较函数返回大于0的数,这样“if”表达式为真,进入下一个交换环节,完美契合冒泡排序机制!

4.元素之间的交换

元素之间的交换常规来说,我们是创建一个临时变量来充当两个元素交换的媒介,但bubble_sort函数就不能这样做,因为我们不知道要用bubble_sort函数排序什么类型的数据,所以更不知道要创建一个什么类型的临时变量。那我们该怎么办呢?

其实也不难!我们可以写一个"swap函数来完成交换功能。我们只要将两个元素的地址和元素类型大小(size)传参给swap函数,这个函数一个字节一个字节的交换内存的内容就可以将两个元素交换位置。这里“size”的作用是知道该交换几个字节的内容。

swap函数如下:

void swap(char* buf1, char* buf2, size_t size)//交换内存内容
{
	size_t i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

好的,bubble_sort函数完整代码如下:

#include<stdio.h>
void swap(char* buf1, char* buf2, size_t size)//交换内存内容
{
	size_t i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* base, size_t num, size_t size, int (*com)(const void* e1, const void* e2))
{
	size_t i = 0;
	for (i = 0; i < num - 1; i++)
	{
		size_t j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (com((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}

	}
}

3.bubble_sort函数的应用 

1.用bubble_sort函数排序整形数组(升序):

#include<stdio.h>
int com_int(const void* e1, const void* e2)//交换函数
{
	return(*(int*)e1 - *(int*)e2);
}
void Print(int arr[], size_t num)//打印整形数组
{
	size_t i = 0;
	for (i = 0; i < num; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void swap(char* buf1, char* buf2, size_t size)//交换内存内容
{
	size_t i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* base, size_t num, size_t size, int (*com)(const void* e1, const void* e2))
{
	size_t i = 0;
	for (i = 0; i < num - 1; i++)
	{
		size_t j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (com((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}

	}
}
int main()
{
	int arr[] = { 5,9,8,7,6,3,5,8,7,6,5,0 };
	size_t count = sizeof(arr) / sizeof(arr[0]);
	Print(arr, count);
	bubble_sort(arr, count, sizeof(arr[0]), com_int);
	Print(arr, count);
	return 0;
}

且看结果:

2.用bubble_sort函数排序结构体(根据结构体成员name排序): 

#include<stdio.h>
#include<string.h>
struct stu
{
	char name[20];
	int weight;
};
int com_structstubyname(const void* e1, const void* e2)//交换函数
{
	return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}
void swap(char* buf1, char* buf2, size_t size)//交换内存内容
{
	size_t i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* base, size_t num, size_t size, int (*com)(const void* e1, const void* e2))
{
	size_t i = 0;
	for (i = 0; i < num - 1; i++)
	{
		size_t j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (com((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}

	}
}
int main()
{
		struct stu student[] = { {"luo",48},{"huang",58},{"jiang",60},{"chen",65} };
		size_t len = strlen(student), i = 0;
		for (i = 0; i < len; i++)//打印排序前结构体成员name
		{
			printf("%s ", student[i].name);
		}
		printf("\n");
		bubble_sort(student, len, sizeof(student[0]), com_structstubyname);
		for (i = 0; i < len; i++)//打印排序后结构体成员name
		{
			printf("%s ", student[i].name);
		}
		printf("\n");
		return 0;
}

且看结果:

4.结尾 

最后,小编觉得可以将bubble_sort函数单独放在一个.c文件中,再在一个.h文件声明bubble_sort函数,这样我们用bubble_sort函数时包含对应.h文件就可以直接使用bubble_sort函数了,就不必将bubble_sort函数具体代码写在主函数那个.c文件中,使代码看起来更加清爽。有兴趣的小伙伴可以去试试哈!

初写博客,如有不足,请多包涵,欢迎斧正!!!跪求点赞,鼠鼠我真的需要你的鼓励捏!

  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
C语言数组排序函数qsort是标准库函数,用于对数组进行排序。它的使用方法是通过传入一个比较函数来指定排序的规则。在给出的代码中,我们可以看到使用qsort函数对一个整型数组进行升序排序的例子。\[1\] 为了实现qsort函数的功能,我们可以使用一种较为简单的排序算法,比如冒泡排序算法来模拟实现一个具有排序数组、字符串、结构体等功能的bubble_sort函数。\[2\]这个函数的参数可以仿照qsort函数的参数,包括要排序的数组的起始地址、元素个数、每个元素的大小以及一个比较函数。\[3\] 具体实现bubble_sort函数函数体可以根据冒泡排序算法来编写,通过比较相邻的元素并交换位置来实现排序。排序的规则可以通过比较函数来指定,根据需要可以实现升序或降序排序。 总结起来,qsort是C语言标准库中的数组排序函数,可以通过传入比较函数来指定排序规则。如果想要模拟实现类似功能的排序函数,可以使用一种简单的排序算法,比如冒泡排序,并根据需要实现相应的比较函数。 #### 引用[.reference_title] - *1* *2* *3* [【C语言qsort()函数详解:能给万物排序的神奇函数](https://blog.csdn.net/weixin_72357342/article/details/130628874)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X_chengonly

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值