一、关于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跳过的字节大小需要由一定了解,以及地址的建立,是怎样的,首元素的地址是首元素的首地址,而不是能说就是首元素的整个地址;
该函数实现,对于指针的用法,大小等知识点需要有一定的了解和熟悉才行;
加油吧!未来可期