模拟库函数qsort实现不同类型元素的冒泡排序

1.库函数qsort

在<stdlib.h>库中有一个qsort函数,用于对数据进行排序,而且没有类型的局限性,可以对任意类型的数据进行排序。在之前我们也写了一个排序算法,冒泡排序,但是之前我们写的冒泡排序只针对整形类型的数据进行排序,所以这次我们来简单模仿qsort来实现一个更高级的冒泡排序。

在cplusplus网站中我们可以找到qsort的介绍

首先我们要了解qsort的返回值和参数类型,他是一个排序函数,所以无返回很容易理解。

第一个参数 void* base ,base是我们所要排序的数据的内存起点,比如我们要排序一个数组,我们要传的是数组名也就是数组首元素的地址。这里的base是 void* 类型的,而void* 可以接受任意类型的指针变量,且 void* 类型的指针不可被解引用,简单来说,void* 是一个泛型指针,我们在传参的时候可以传任意类型的指针,base都能接受,等到要用的时候再把它强制转换成所需要的类型的指针来解引用。

第二个参数 size_t num ,  num是我们要排序的元素的个数,size_t 就是unsigned int 的重命名。

第三个参数 size_t size , size 是要排序的每个元素的内存大小。到这里我们大概就知道了qsrot应该是要一段内存一段内存比,一段内存就是一个元素的大小,如何进行排序。

第四个参数 int ( *compar ) ( const void * ,const void *) , 这是一个函数指针,看参数命名应该是一个比较函数的函数指针,我们可以详细看一下这个参数的介绍。

在介绍中说了,这个比较函数会在qsort中重复调用来比较两个数据,这个函数的返回类型为int,当p1 的内容大于p2的内容时,返回一个大于零的数,当p1的内容等于p2的内容时,返回零,否则返回小于零的数。在下面的例子中我们看到compar函数的内部是先把两个泛型指针强制转换成我们所需要比较的元素的类型的指针,在这里我们就恍然大悟了,原来qsort函数能够排序不同类型的数据的根源在这里。

2.练习使用qsort

了解了qsort的参数和返回类型我们就要练习使用sqort,这个函数最重要的地方就在于比较函数。

当我们要排序一个 int 类型的数组时,我们的比较函数就应该将指针强制转换成int 类型来比较,其他的类型排序也是如此,在比较函数里强制转换。

#include<stdio.h>
#include<stdlib.h>


int cmp(const void* a, const void* b)
{
	return *(int*)a - *(int*)b;
}
int main()
{
	int arr[10] = { 2,6,7,8,9,4,1,2,5,10 };
	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), cmp);
	int i = 0;
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

当我们写下这段代码的时候,qsort会将arr数组的内容排成升序。

我们写的比较函数是直接返回了a和b强转成int类型之后的差值,因为强制转换成int类型,而cmp需要的也是int类型的返回值,如果比较的是其它类型的值,我们要强制转换成int类型再返回,或者直接判断大小然后返回1、0、-1这样的值。就比如我们要排序的是double类型的值,我们可以这样写cmp函数

int cmp_for_double(const void* a, const void* b)
{
	if (*(double* ) a > *(double* ) b)
	{
		return 1;
	}
	else if (*(double*)a == *(double*)b)
	{
		return 0;
	}
	else
	{
		return -1;
	}
}

当我们这样写的时候排的是升序,如果我们想要排成降序也很简单,只要把cmp的逻辑反过来就行了。当 * ( my_type) a > *(my_type)b 的时候,我们返回负数,相等的时候还是返回 0 ,否则返回1。这样就能得到降序了。

int cmp_for_double(const void* a, const void* b)
{
	if (*(double* ) a > *(double* ) b)
	{
		return -1;
	}
	else if (*(double*)a == *(double*)b)
	{
		return 0;
	}
	else
	{
		return 1;
	}
}
int main()
{
	//int arr[10] = { 2,6,7,8,9,4,1,2,5,10 };
	double arr[10] = { 1.1,3.3,9.9,2.2,4.4,5.5,7.7,8.8,6.6,10.2 };
	//qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), cmp_for_int);
	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), cmp_for_double);
	int i = 0;
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		//printf("%d ", arr[i]);
		printf("%f ", arr[i]);
	}
	return 0;
}

3.实现bubble_sort

了解完qsort之后我们就要开始实现冒泡排序了。参数部分我们与qsort保持一样,函数内部的逻辑需要我们自己去思考。

与之前的冒泡排序一行,我们还是需要排序num-1躺,每趟比上一趟少一对比较,这个大的思路是没有变的。最重要的就是我们怎么进行比较和交换。

首先,我们是知道每个元素的size的,而cmp函数也是知道我们要比较的数据类型是什么的,所以我们只需要传两个元素的首地址给cmp,cmp就能返回值。如果这个值小于等于0,说明这两个元素顺序是对的,不用交换,如果返回值大于0,就对两个元素进行交换。交换的部分我们也要注意,因为我们是不知道具体类型的,所以我们选择强制转换成char*类型的指针后逐字节交换。在这期间,由于我们需要base不断往后走,所以我们要先保存初始位置的指针,每一趟排序base都要回到初始位置。

实现的代码如下:

void bubble_sort(void* base, size_t num, size_t size, int(*cmp)(const void*, const void*))
{
	assert(base);
	int i = 0;
	int j = 0;
	void* begin = base;
	for (i = 0; i < num - 1; i++)
	{
		base = begin;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base, ((char*)base + size)) > 0)
			{
				//交换每个字节的内容
				int i = 0;
				for (i = 0; i < size; i++)
				{
					char tmp = *((char*)base + i);
					*((char*)base + i) = *((char*)base + size + i);
					*((char*)base + size + i) = tmp;
				}
			}
			base = (char*)base + size;
		}
	}
}

代码这样写 bubble_sort 函数的逻辑就实现了,我们可以用qsort用过的例子来检测这个函数是否能够完成任务。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>


int cmp_for_int(const void* a, const void* b)//排升序的int类型的cmp函数
{
	assert(a && b);
	return *(int*)a - *(int*)b;
}

int cmp_for_double(const void* a, const void* b)//排降序的double类型的cmp函数
{
	assert(a && b);
	if (*(double* ) a > *(double* ) b)
	{
		return -1;
	}
	else if (*(double*)a == *(double*)b)
	{
		return 0;
	}
	else
	{
		return 1;
	}
}



void bubble_sort(void* base, size_t num, size_t size, int(*cmp)(const void*, const void*))
{
	assert(base);
	int i = 0;
	int j = 0;
	void* begin = base;
	for (i = 0; i < num - 1; i++)
	{
		base = begin;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base, ((char*)base + size)) > 0)
			{
				//交换每个字节的内容
				int i = 0;
				for (i = 0; i < size; i++)
				{
					char tmp = *((char*)base + i);
					*((char*)base + i) = *((char*)base + size + i);
					*((char*)base + size + i) = tmp;
				}
			}
			base = (char*)base + size;
		}
	}
}

int main()
{
	//int arr[10] = { 3,5,8,7,4,2,1,9,6,11 };
	double arr[10] = { 1.1,3.3,9.9,2.2,4.4,5.5,7.7,8.8,6.6,10.2 };
//	bubble_sort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), cmp_for_int);//整型升序
	bubble_sort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), cmp_for_double);//double降序

	int i = 0;
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		//printf("%lf ", arr[i]);//打印double数组
		printf("%lf ", arr[i]);//打印整型数组
	}

	return 0;
}

这样我们就完成了对bubble_sort的升级,虽然我们不知道vs库里面的qsort函数具体是怎么实现的,但是我们从参数和使用qsort函数的过程中是能够想出这一种思路的。

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值