一.参数
要实现全类型冒泡排序,函数的参数应该使用void*,表示接受任意类型,我们还要知道要排序数
组的元素个数,使用size_t,由于我们不知道要排序什么类型的数组,如果是一个结构体数组,那该如何
排序,所以我们需要使用者自己写一个比较函数.这样使用者也可以根据自己的想法来选择排序方式.
比如递增排序,递减排序,后面只需调用这个函数,把较大的交换即可,但我们不知道要交换的数据的类
型,所以我们使用指针的方式交换数据,为了能够交换所以的数据类型,我们还需要知道这种类型的大
小,最后这个函数是操作数组,不需要返回值,最后得出
void bubble_sort(void* arr, size_t num, size_t size, int(*comp)(const void* i, const void* j))
四个参数,第一个接受任意类型的数组,第二个是数组的元素个数,第三个是每个元素的大小,第
四个是一个函数指针,指向一个使用者自己写的比较函数,
二.内部实现
内部的实现非常简单,就是一个普普通通的冒泡排序,通过使用者自己提供的比较函数,要求这个
函数,传入数组两个数的地址,比如如果是Int类型的数组,我们只需arr+i;就能找到这个数的地址,但由
于我们不知道是什么类型,无法确定地址,但实际上,比如一个Int类型的数组,因为Int是四个字节,所以
我们可以这样表示,
我们知道指针加上或减去一个整数,它的值与指针的类型有关系,比如int类型的指针加上1,就会
跳过四个字节,char类型的指针加1,则会跳过1个字节,所以我们可以将其转换为char*类型找到地址,
&arr[i]=arr+i=(int*)((char*)arr+i*4)
void bubble_sort(void* arr, size_t num, size_t size, int(*comp)(const void* i, const void* j))
{
for (int i = 0; i < num - 1; i++)
{
for (int j = 0; j < num - 1 - i; j++)
{
if (comp((char*)arr + j * size, (char*)arr + (j + 1) * size) > 0)
{
swap((char*)arr + j * size, (char*)arr + (j + 1) * size, size);
}
}
}
}
我们重点看一下是如何对任意类型元素交换位置,
三.内部函数
从上面我们可以看出,swap这个函数,有三个参数
void swap(char* i, char* j, size_t size);
接受两个要交换的元素的地址,还有元素的大小,由于我们不知道是什么类型,那该如何交换呢
还是要使用char*类型辅助,char*类型加1,就是移动一个字节,把要交换的元素的地址每个字节都交换
一下不就行了吗
void swap(char* i, char* j, size_t size)
{
for (int n = 0; n < size; n++)
{
char a = *i;
*i=*j;
*j = a;
i++, j++;
}
}
四.举例
排序数组
比如我们要使用这个函数递减排序一个整型数组,那我们就把比较函数写成这样
int comp(const void* i, const void* j)
{
return *(int*)j - *(int*) i;
}
排序结构体
要排序结构体,我们完全可按照我们自己想的方式排序
比如我们定义这个结构体
typedef struct Stu
{
char name[20];
int age;
}Stu;
我想要按照年龄从小到大来排序
我就可以把比较函数写成这样
int cmp_stu_age(const void* e1, const void* e2)
{
return ((Stu*)e1)->age - ((Stu*)e2)->age;
}
我想要从姓名的字母排序
我就可以把比较函数写成这样
int cmp_stu_name(const void* e1, const void* e2)
{
return strcmp(((Stu*)e1)->name, ((Stu*)e2)->name);
}
想要怎样排序只在于使用者自己写的比较函数,与快排函数qsort类似