模拟实现qsrot函数

一、关于qsrot函数:

1、简介:

       qsrot函数是c语言中对各种类型的数组元素进行快速排序的函数,将元素以小到大排序;

需要用🫵头文件:

#include<stdlib.h>

2、函数定义:

如下,第一个形参为待排序数组的第一个元素,第二个形参为数组元素个数,第三个为base指向数组的元素大小,第三个为函数指针,指向的就是两个元素进行比较的函数(存放函数地址),且若这个位置的元素大于后一位的元素的话,返回>0,等于则返回0小于则返回<0的数

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

3、如何使用(以int型数组为例子)

1>整体i编辑

先建立main函数,那么开始思考,还有一个传函数的地址的位置空着的,这时就要思考这个函数怎么实现;

int main()
{
	int arr[] = { 1,4,5,9,8,7,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int sz1 = sizeof(arr[0]);

	qsort(arr, sz, sz1, );

	return 0;
}

2>建立一个函数,用来实现两个元素比较

就是设2个变量,形参类型保证和qsrot里面的保持一致;

int cmp_int(const void* p1, const void* p2)
{

	return *(int*)p1 - *(int*)p2;
}

因为形参时void*型的,可以接受各种指针。但是我在指针就说过void*型指针不能进行解引用,所以我用到了强制类型转换,将void* 转换为 int*(和传参数组相同类型)在进行解引用;直接解引用相减即可,不同类型写法稍有不同,如char型:可以用字符串比较函数strcmp()来编写函数;总之方法很多,只要满足这个函数的需求即可;

:若是这样写->

int cmp_int(const void* p1, const void* p2)
{

	return  *(int*)p2- *(int*)p1;
}

就会打印逆序排列的数组;

3>打印数组函数

这个函数就相对简单多了

//sz为数组元素个数
void prin_t(int *p,int sz)
{
	int i;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", p[i]);
	}
}

4>整理:

int cmp_int(const void* p1, const void* p2)
{

	return  *(int*)p2- *(int*)p1;
}
void prin_t(int *p,int sz)
{
	int i;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", p[i]);
	}
}
int main()
{
	int arr[] = { 1,4,5,9,8,7,6 };
    //求数组元素个数
	int sz = sizeof(arr) / sizeof(arr[0]);
    //求每个元素宽度(长度)
	int sz1 = sizeof(arr[0]);
    //传参
	qsort(arr, sz, sz1, cmp_int);
	//打印排序后数组
    prin_t(arr,sz);

	return 0;
}

二、模拟实现qsrot函数:

理解了qsrot传参经过后就可以进行模拟实现了

1、定义函数:

//base为接收数组的指针,num为数组元素个数,wigt为每个元素宽度,*compar接收的是 2个元素进行比较的函数 地址
void my_qsort(void* base, size_t num, size_t wigt, int (*compar)(const void*, const void*))

2、排序方法:

qsrot里面的排序要如何实现了,我们可以想到用类似冒泡排序的方法;(我称它为类冒泡

for (i = 0; i < num - 1; i++)
{
	int j;
	for (j = 0; j < num - i - 1; j++)
	{
    
    }

但是,里面的内容不能用了,因为冒泡排序针对的对象时int型,而qsort对于各种类型都要支持;

3、怎么完善类冒泡排序:

1>交换的前提

        不可能是任何时候都要交换的,当相等时或者第j项比j+1项小时是可以不用交换的,也就是可以加一个if语句;

        但是,不能直接进行*(base+1),因为这个是void*变量;不能这样用,不能确定传参数组的类型,也不能直接强制性转换,因此我们要想想,还有什么参数没有用到,那就是宽度;我们想想什么类型指针加一只会跳过一个字节,你可以去自己编译不同指针计算加1的值,很明显char*就是要转变类型,既然这样的,char*指针加宽度,不就是下一个元素的地址么!!!用函数指针来存放变量;compar就是相当于用来比较函数函数别名(因为函数指针引用时候可写可不写前面的))

if (compar((char*)base + j * wigt, (char*)base + (j + 1) * wigt)>0)
{

}

2>进行交换: 

同样的,常规的直接数组进行交换明显行不通了;直接指针进行交换呢??图画的不是很好

(char*)base存放地址是数组首元素的地址的首个地址(一个元素地址的个数是根据类型来看的,但是取地址,取的首地址);因此只(char*)base + j * wigt(char*)base + (j + 1) * wigt交换是不行的,只是交换了两个元素首地址,那么会出现错误,根本找不到指向的内容,因此可以这么设计(就好比如老师让你打扫卫生,给你了一区域(相当于一个元素),你只知道这个区域的开始地方,不知道结束位置,另一个值班同学也在打扫卫生,他在另一个区域,他和你交换打扫区域,可是应该打扫到哪里,你不知道,那他怎么可能和你换,要是打扫的区域比他大呢?这不就是不公平么

//wigt接受数组每个元素宽度
void wop(char* p1, char* p2,int wigt)
{
	int i;
	for (i=0;i<wigt;i++)
	{
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		*p1++;
		*p2++;
	}
}

3>完整的代码就来了;

for (i = 0; i < num - 1; i++)
{
	int j;
	for (j = 0; j < num - i - 1; j++)
	{
		
		//当两个元素比较时若 >0 ,就进行交换,知道宽度,base为首元素地址加元素的宽度*j就是下标为j的元素地址
		//char*的指针+1 跳过1个字节;
		if (compar((char*)base + j * wigt, (char*)base + (j + 1) * wigt)>0)
		{
			//开始交换,常规的交换行不通,因为只是交换了一下地址
			wop((char*)base + j * wigt, (char*)base + (j + 1) * wigt,wigt);
		}
	}
}

4、整个函数的代码:

模拟实现快速排序函数,qsort()
void wop(char* p1, char* p2,int wigt)
{
	int i;
	for (i=0;i<wigt;i++)
	{
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		*p1++;
		*p2++;
	}
}
 
//base为接收数组的指针,num为数组元素个数,wigt为每个元素宽度,*compar接收的是 2个元素进行比较的函数 地址
void my_qsort(void* base, size_t num, size_t wigt, int (*compar)(const void*, const void*))
{
	//排序,可以由类似冒泡排序进行确定
	int i;
	for (i = 0; i < num - 1; i++)
	{
		int j;
		for (j = 0; j < num - i - 1; j++)
		{
			
			//当两个元素比较时若 >0 ,就进行交换,知道宽度,base为首元素地址加元素的宽度*j就是下标为j的元素地址
			//char*的指针+1 跳过1个字节;
			if (compar((char*)base + j * wigt, (char*)base + (j + 1) * wigt)>0)
			{
				//开始交换,常规的交换行不通,因为只是交换了一下地址
				wop((char*)base + j * wigt, (char*)base + (j + 1) * wigt,wigt);
			}
		}
	}

}

三、实现的过程中的感受

        对于关于各种类型指针加1跳过的字节大小需要由一定了解,以及地址的建立,是怎样的,首元素的地址是首元素的首地址,而不是能说就是首元素的整个地址;

        该函数实现,对于指针的用法,大小等知识点需要有一定的了解和熟悉才行;

加油吧!未来可期

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值