模拟实现qsort排序

 如图,我们可以看出qsort的库函数参数有void*base(要排序元素的地址),size_t  num(排序元素个数,与排序次数相关),size_t  size(排序单个元素的大小),第四个参数是最有意思的,是一个比较函数指针,前两个参数是普通的排序函数必备的,至于为什么会有后面两个参数,我们接下来一步步模拟就清楚了。


qsort_t(int arr[], int sz)
{
	for (i = 0; i < sz-1; i++)//要交换的趟数
	{
		int j = 0;
		for (j=0;j<sz-1-i;i++)//要排序的元素,每排序成功一次,需要排序的元素-1
		{
			if (arr[j]>arr[j+1])//如果前面比后面大,就交换
			{
				int tmp = arr[j];
				arr[j] = arr[j+1];//交换
				arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	int arr[10] = {9,8,7,6,5,4,3,2,1,0};
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort_t(arr,sz);
	return 0;
}

   设置比较函数指针(qsort参数四)原因

     当我们要排序整型数组时,我们自己写了个qsort_t的函数,其内部用的就是一个个比较,然后交换的方法,如果要排列两个不同的字符串呢,比如"he"和"li",还能用arr[j+1]>arr[j]比较吗,很明显不可以,这说明我们此时的比较方法并不适用于字符串的比较,所以我们比较不同元素时我们要书写不同的比较函数,但是库函数qsort的书写者是不知道比较元素的类型,更无法写出对应的比较方法,所以就设置了比较函数指针这个参数,我们自行书写一个比较函数传给qsort,qsort内部用函数指针调用比较函数完成比较操作,这样这个qsort函数的通用性就很高。


 void Qsort_p(void* base, size_t sz, size_t size, int(*int_cmp)(void*, void*))
{
	int i = 0;
	for (i = 0; i < sz-1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{      
                   //当int_cmp返回值大于零,也就是当前面的元素大于后面的元素时进行交换,使得数据呈现为升序  
			if (int_cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0)
			{
				swap_p((char*)base + j * size, (char*)base + (j + 1) * size,size);
			}
		}
	}
}

比较函数的参数

    可以看到在循环内部我们通过函数指针的方式调用了比较函数,那传什么参数给比较函数呢?我们目前只有待排序元素的起始地址,就像是1,2,3,4这四个整型数字中我们只有1的地址,类型还是void*,如何定位到2的地址呢?首先我们的地址一开始是用void*指针接收,是没有步长的,所以我们需要强制类型转换,这里我们强制类型转换为char*,加一跳过一个字节,很明显只要我们的地址跳过整型类型的大小个字节就可以来到数字2的地址,如果我们不知道排序元素的类型,又如何知道需要跳过的字节数?显然,这个数据需要我们从外界传给我们模拟的qsort,这就是size(qsort参数3)参数设置的原因,选择强制类型转为char*是为了方便,这一点希望大家可以理解理解,所以我们当第一次排序第一次比较的时候,我们就比较第一个数据和第二个数据,然后看看要不要交换,随着循环的继续,j逐渐增加,再比较第二个数据和第三个数据,比较元素的地址就是用j和base配合,将所有元素比较一遍。

     补充:比较函数指针是有类型的,函数指针的类型就是返回值类型+()+(参数类型),由于我们不知道参数指针类型,所以我们设比较函数参数为void*,排序元素地址用void*指针接收也是如此。

比较函数的返回值选择:

int int_cmp(void* p1, void* p2)
{
	return(*(int*)p2 - *(int*)p1); 如果比较两个整型大小,返回值为前一元素减后一元素值 
}
    此时因为p2接收的是后面的元素地址,所以当后面的元素大于前面的元素的时候,
      元素相减返回值大于零,进入if内部交换,导致结果为降序,
      想要升序,相减顺序调换一下就可以了。        
      
      

     swap_p函数参数和交换原理解析  

     由于我们是在模拟的qsort中调用swap_p函数,所以我们除了传要两个交换元素的地址还要额外传一个size,不然交换的时候无法知道什么时候交换结束,size记录了我们要排序元素的大小,我们也就知道一次要交换的总字节数,这个时候如果我们把void*强制类型转换为char*, 一个个字节交换,交换完一个元素大小的字节就好了,随着内层循环j的增加,我们就可以交换所有的元素。


void swap_p(void*p1,void*p2, int size)
{
	int i = 0;
	for (i = 0; i < size; i++)//交换完一个元素大小的字节,一次元素交换结束
	{
		char cmp = *((char*)p2+i);
		*((char*)p2 + i)=*((char*)p1+i);//一个个字节交换
		*((char*)p1 + i)= cmp;
	}
}

qsort很多设计都是与后面运行相关,一开始说不清楚,需要结合后面来看,第一次尝试讲述函数设计原理,还有许多不足,欢迎大家私信!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小何只露尖尖角

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

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

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

打赏作者

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

抵扣说明:

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

余额充值