自定义冒泡排序
自定义这个排序的底层思想就是使用了冒泡排序的思想,但冒泡排序具有局限性,它只能排序整型类型的数据,而不能实现对浮点型,字符,字符串,结构体这些类型的数据进行排序,所以我们可以自定义一个冒泡排序来实现对这些类型的数据的排序。
自定义冒泡排序,我们可以参照一下qsort函数,该函数是库函数也是可以排序任意类型的数据,该函数的定义如下:
qsort (void * base,size_t num,size_t size,int (*cmp)(const void*,const void*));
//void* base:指向的是待排序数组的第一个元素的指针
// zise_t num:指向数组中元素的个数
// zise_t size:指向的是数组中一个元素的大小,单位是字节
// int (*cmp) (const void*,const void*) :函数指针——用来接收函数的地址
对qsort函数不太熟悉的,可以去看一下我上一篇的文章,看了之后对这篇文章的学习也更简单。
了解了qsort函数的定义,我们就可以模仿它来实现bubble_sort函数的定义了,定义如下:
bubble_sort(void * base,size_t sz,size_t width,int (*cmp)(const void*,const void*))
定义好了之后,我就可以开始写主函数的部分了,我们就先来排序整型类型的数据吧,先定义一个整型数组:
int main()
{
int arr[] = {3,4,2,1,7,0,5,6,9,8};
size_t sz = sizeof(arr) / sizeof(arr[0]);
size_t width = sizeof(arr);
bubble_sort(arr,sz,width,cmp_int);
for(int i = 0;i < sz;i++)
{
printf("%d ",arr[i]);
}
return 0;
}
主函数部分写完之后,接下来就是调用bubble_sort函数的部分,也就是对这些数据进行排序,因为底层思想是冒泡排序的思想,所以会用到冒泡排序,我们可以先把冒泡排序的代码写出来:
void bubble_sort(arr,size_t sz)
{
for (int i = 0;i < sz - 1;i++)
{
for (int j = 0;j < sz - 1 - i;j++)
{
if(arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
我们可以看到,该排序只能实现对整型类型的数据的排序,并没有达到我们想要的效果,所以我们要在该代码的基础上进行改变,代码如下:
bubble_sort(void * base,size_t sz,size_t width,int (*cmp)(const void*,const void*))
//void*的作用是接收任意类型的指针
{
for (int i = 0;i < sz - 1;i++)
{
for (int j = 0;j < sz - 1 - i;j++)
{
if (cmp( (char *)base + j * width,(char*)base + (j + 1) * width) > 0)
{
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
//这里就是对两个数进行交换了
//width是要传过去的,这里交换的本质是两个数的字节在交换
}
}
}
}
这里对第八行if语句的代码进行说明一下:
if (cmp( (char *)base + j * width,(char*)base + (j + 1) * width) > 0)
//这里是将两个要进行比较的数的地址传给cmp指向的函数cmp_int
//然后这两个数在cmp_int函数中进行比较,返回值如果大于0,就进行两个数的交换
//对于(char*)base + j * width 和 (char*)base + (j + 1) * width的理解,我给大家画了个图在下面
上述代码的源码:
void swap(char* buf1, char* buf2, size_t width)
{
for (int i = 0;i < width;i++)
//有多少个字节就交换多少次
{
char temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
}
int cmp_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
//这里需要将void*强制转换的,因为void*是不能进行解引用和运算操作的
//如果你需要排序的是整型就强制转换成(int*),如果是字符型就转换成(char*)
//对于两个字符的比较需要用到的是strcmp函数
}
void bubble_sort(void * base, size_t sz,size_t width,int (*cmp)(const void*,const void*))
{
for (int i = 0;i < sz - 1;i++)
{
for (int j = 0;j < sz - 1 - i;j++)
{
if (cmp( (char *)base + j * width,(char*)base + (j + 1) * width) > 0)
{
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
int main()
{
int arr[] = { 3,4,2,1,7,0,5,6,9,8 };
size_t sz = sizeof(arr) / sizeof(arr[0]);
size_t width = sizeof(arr[0]);
bubble_sort(arr, sz, width, cmp_int);
for(int i = 0;i < sz;i++)
{
printf("%d ",arr[i]);
}
return 0;
}
用上述源码排序结构体,只需在上述码中添加想要排序的数据即可,代码如下:
struct stu
{
char name[20];
int age;
};
void swap(char* buf1, char* buf2, size_t width)
{
for (int i = 0;i < width;i++)
{
char temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
}
int cmp_stu_by_name(const void* p1, const void* p2)
{
return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);
}
void bubble_sort(void * base, size_t sz,size_t width,int (*cmp)(const void*,const void*))
{
for (int i = 0;i < sz - 1;i++)
{
for (int j = 0;j < sz - 1 - i;j++)
{
if (cmp( (char *)base + j * width,(char*)base + (j + 1) * width) > 0)
{
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
void print_name(struct stu arr[], size_t sz)
{
for (int i = 0;i < sz;i++)
{
printf("%s %d ",arr[i].name,arr[i].age);
}
}
int main()
{
struct stu arr[] = { {"zhangsan",18},{"lisi",19},{"wangwu",20} };
size_t sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
print_name(arr, sz);
return 0;
}
该代码是按照结构体的名字进行排序的,也就是按照字符串进行排序,可以看到,在比较和交换的这两个部分的代码是不变的,我们只需要把我们要排序的数据和把两个数比较的结果返回回去即可。这个自定义冒泡排序的代码就可以应用于任意类型的数据了。
希望看了这篇文章的你们会有所收获,有错误或不懂的可以提出,一起学习!!