这是最简单的冒泡排序写法,但是我们好像发现这种写法只能排序固定的类型,如果要写一个可以排任意类型的冒泡排序,我们要怎么写呢?
int main()
{
int arr[] = { 1,2,3,4,5 };
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz -1; i++)
{
for (int 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;
}
}
}
return 0;
}
这时候我们就要使用回调函数的写法了。回调函数可以让我们无需关心数据的类型,只做排序的工作即可。
冒泡排序的思想还是和原来一样,关键是如何进行前后两个元素的比较。我们知道两个不同类型的数据是不能一起比较的,字符串之间也不能用大于小于等于来比较,因此我们要参考qsort的思想,传入一个比较函数。
void Bsort(void* base, size_t num, size_t width, int(*cmp)(const void*, const void*))
{
for (size_t i = 0; i < num - 1; i++)
{
for (size_t j = 0; j < num - i - 1; j++)
{
if ((cmp((char*)base+j*width,(char*)base+(j+1)*width))>0)
//base是void*,无法进行运算。因此强制转换成char*
,加1*width即跳过一个对应的类型
{
swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
}
}
}
}
上面这段代码的难点是cmp函数里面的参数。我们知道,冒泡排序里面cmp传入的是前后两个元素,即第j个和第j+1个元素。
转换成char*的原因是char*的精细度最高,若转换成int*,就无法比较char类型了
接下来是交换函数
void swap(char* e1, char* e2,int width)
{
for (int i = 0; i < width; i++)
{
char tmp = *e1;
*e1 = *e2;
*e2 = tmp;
e1++;
e2++;
}
}
我们可以把两个元素对应的补码的每一个字节进行交换。
这种方法可以记住,是通用的。
完整的代码
void swap(char* e1, char* e2,int width)
{
for (int i = 0; i < width; i++)
{
char tmp = *e1;
*e1 = *e2;
*e2 = tmp;
e1++;
e2++;
}
}
void Bsort(void* base, size_t num, size_t width, int(*cmp)(const void*, const void*))
{
for (size_t i = 0; i < num - 1; i++)
{
for (size_t j = 0; j < num - i - 1; j++)
{
if ((cmp((char*)base+j*width,(char*)base+(j+1)*width))>0)//base是void*,无法进行运算。因此强制转换成char*,加1*width即跳过一个对应的类型
{
swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
}
}
}
}
附几种常用的比较函数:
int cmp_int(const void* e1, const void* e2)//整型
{
return *(int*)e1 - *(int*)e2;
}
int cmp_str(const void* e1, const void* e2)//字符指针数组(比较字符串
{
return strcmp(*(char**)e1, *(char**)e2);
}
int cmp_stu_name(const void* e1, const void* e2)//比较结构体的成员
{
return strcmp(((stu*)e1)->name, ((stu*)e2)->name);
}