初识qsort函数

qsort函数C语言编译器函数库自带的排序函数,是基于快速排序的一种函数

位于 stdlib.h

语法

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

base :指向要排序的数组的第一个元素的指针。

nitems : base指向得到数组中元素的个数。

size:数组中的元素大小,以字节为单位。

cmpar :用以比较的函数

例子

//整型比较函数
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
int main()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	print(arr, sz);
	return 0;
}

在使用qsort时,要为qsort函数提供一个函数用以给qsort提供比较的方法。

qsort是一个通用的排序函数,在排列不同的的数组时,不同元素的比较方法差异甚大,qsort函数本身并未提供比较方法,需要自己声明定义一个比较的函数。

因涉及到传参时存在函数参数,需保证所定义的比较函数的 返回值,参数类型保持一致。

int (*compar)(const void *, const void*)

因排序数组元素的类型并不确定,故cmpar参数中,均使用的是 void类型的指针。以方便不同元素传址时,地址的存储。

qsort函数默认排序是排正序的,当compar函数返回正数时,便会交换所比较的两个元素。可以通过对于比较函数的调整,实现倒序,以上文例子为例

int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e2 - *(int*)e1;
}

冒泡排序模拟实现qsort

qsort 是基于快速排序的程序,通过冒泡排序来模拟实现qsort功能,模拟出一个基于冒泡排序的qsort

冒泡排序分析

在模拟之前先复习一下冒泡排序

void bubble_sort(int* arr, int sz)
{
	int i = 0;
	int j = 0;
	//主体
	for (i = 0; i < sz - 1; 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;
			}
		}
	}
}

通过各元素依次比较,最后的到完成对数组的排序

对冒泡排序进行分享,不难发现整个排序可以被划分为三个部分

主体部分 :冒泡排序的主要思想

比较部分 : 判断是否需要交换,需根据排序的需求而改变

交换部分 : 将两个变量的数据进行互换

qsort参数分析

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

qsort的参数数量远大于整型的冒泡排序,一个终于的原因是,整型的冒泡排序需要排列的数组中元素的类型是确定的,而qsort是不知道所需排列的变量类型的。从这个角度出发,理解qsort的参数便容易了许多。

base: 因不知变量类型,故只能用一个void* 型的空指针接受数组首元素地址。

nitems, size: 因数据在内存中是以字节为单位存储的,而不同类型的数据所需的字节数也不同,通过 nitems确定数组的元素个数,size确定每个元素所占空间的大小。便能很清晰的描述一个数组。

compar :是一个函数指针,为保证所有类型元素的数组都能进行比较,一是比较方法需自行设计,二是为了保证函数传址,所有函数需满足统一规则(返回值为int ,参数类型为两个const void*,当返回值大于零时,交换元素)

模拟实现

因是准备基于冒泡排序实现qsort的功能

故函数主体是冒泡排序

//通过bubble_sort模拟实现qsort
void my_qsort(void* arr, int sz, int width, int(*cmp)(void*, void*))
{
	//bubble_sort的主体
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - i - 1; j++)
		{

		}
	}
}

而相对于整型的冒泡排序而言,比较部分与交换部分则复杂了许多。无法直接进行比较了。

就如上文提到的,比较部分需用户自行设置,本次就以整型,结构体为例

整型的比较函数(正序情况下)

int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

为了保证函数传址的进行,参数类型已被限制死。就行规则中确定的,当返回值大于0时,将交换两个函数。将e1强制类型转化为要比较的类型,解引用后便可进行运算,来给与函数所需的判断规则了。

结构体的比较函数(正序情况下)

//结构体的定义,并将结构体 struct Stu 重命名为Stu
typedef struct Stu
{
	char name[1000];
	int ID;
	int age;
}Stu; 
//Stu结构体通过name比较函数
int cmp_struct_name(const void* e1, const void* e2)
{
	return strcmp((*(Stu*)e1).name, (*(Stu*)e2).name);
}
//Stu结构体通过age进行比较的函数
int cmp_struct_age(const void* e1, const void* e2)
{
	return (*(Stu*)e1).name- (*(Stu*)e2).name;
}

写完比较函数后,反观函数本体,函数本身是不知数组中元素的类型的,只值元素所占字节大小,和元素数量。每一次比较都需要传给比较函数两个元素,可以通过元素大小与元素数量的结合,实现参数的传递

void* e1 = (char*)arr + j * width;
void* e2 = (char*)arr + (j+1) * width;

当比较通过后,便需要根据比较的结构,判断是否需要交换

void my_qsort(void* arr, int sz, int width, int(*cmp)(void*, void*))
{
	//bubble_sort的主体
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - i - 1; j++)
		{
			//比较部分
			void* e1 = (char*)arr + j * width;
			void* e2 = (char*)arr + (j+1) * width;
			if (cmp(e1, e2) > 0)
			{
				//交换部分
			}
		}
	}
}

交换部分也因不知元素类型变得复杂,不妨分装函数来解决这个问题

交换两个元素需要知道他们的地址,虽然不知元素的类型,但可以通过宽度来解决这个问题

不管是什么类型的元素,总是以字节为单位存储在内存中的数据,可以通过交换两个元素相互对应的数据,以字节为单位交换。

void my_swap(void* const e1, void* const e2,int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *((char*)e1+i);
		*((char*)e1+i) = *((char*)e2+i);
		*((char*)e2+i) = tmp;
	}
}

到此基于冒泡排序模拟的qsort已经完工了,整体代码如下

//交换函数
void my_swap(void* const e1, void* const e2,int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *((char*)e1+i);
		*((char*)e1+i) = *((char*)e2+i);
		*((char*)e2+i) = tmp;
	}
}
//通过bubble_sort模拟实现qsort
void my_qsort(void* arr, int sz, int width, int(*cmp)(void*, void*))
{
	//bubble_sort的主体
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - i - 1; j++)
		{
			//比较部分
			void* e1 = (char*)arr + j * width;
			void* e2 = (char*)arr + (j+1) * width;
			if (cmp(e1, e2) > 0)
			{
				//交换部分
				my_swap(e1, e2,width);
			}
		}
	}
}

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值