目录
前言
冒泡排序算法
库函数qsort函数
自定义bubble_sort排序函数
test()测试函数
bubble_sort自定义冒泡函数
cmp_int比较函数
Swap()交换函数
我们前边学习了指针的一些高端操作,学会了函数指针的用法,我们今天的任务就是来通过函数指针来模拟库函数qsort的实现。
冒泡排序是一种较为基础的排序算法,它通过相邻元素的比较来实现排序,但是冒泡排序的缺点就是只能进行整型元素的排序,这一点也是一个限制条件。
冒泡排序代码:
void bubble_sort(int arr[], int sz)
{
//趟数
int i = 0;
for (i = 0; i < sz - 1; i++)
{
//一趟冒泡排序的过程
int j = 0;
for (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;
}
}
}
}
通过给函数传入要排序的整型数组和数组的长度就可以实现对整型元素的排序。
我们刚提到冒泡排序的限制条件是不能对整型以外的数据进行排序,那么有没有其他排序方式呢?答案是有的,C语言的stdlib库中为我们提供了qsort函数(内部是快速排序),我们来讲解一下qsort函数的使用方法。
在对qsort函数的解释中,我们可以看到,它需要四个参数,
第一个参数指向要排序的数组的第一个对象的指针,转换为 void*
第二个参数数组中由 指向的元素数,是无符号整型,unsigned int,size_t
第三个参数数组中每个元素的大小(以字节为单位),是无符号整型,size_t
第四个参数指向比较两个元素的函数的指针,重复调用此函数以比较两个元素。它应遵循以下原型:int (*compar)(const void*,const void*)
第四个参数就是我们最近学习的函数指针,那么此时我们就需要定义一个函数,返回值为int型,通过这个函数,就可以实现多种类型数据的排序。
将两个指针作为参数(都转换为常量 void*)。该函数通过返回(以稳定和传递的方式)来定义元素的顺序:
返回值 | 意义 |
---|---|
<0 |
指向的元素 在指向的元素之前p1p2 |
0 |
指向的元素等效于p1p2 |
>0 |
指向的元素在指向的元素之后p1p2 |
qsort库函数代码:
//实现一个比较整型的函数
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
void test()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
//bubble_sort(arr, sz);
//库函数中一个排序函数:qsort
qsort(arr, sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
test();
return 0;
}
在冒泡排序和qsort库函数排序的基础上,我们就可以自定义一个函数来实现多类型数据的排序。
首先,我们定义test()函数,将数组的定义以及数组排序后打印放在这个函数中,然后调用我们自定义的排序函数。
//使用我们自己写的bubble_sort函数排序整型数组
void test()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main() {
test();
return 0;
}
我们自定义的bubble_sort函数也有四个参数,与库函数qsort函数的参数是相同的,接下来实现bybble_sort函数
void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
//趟数
size_t i = 0;
for (i = 0; i < sz - 1; i++)
{
//一趟冒泡排序的过程
size_t j = 0;
for (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);
}
}
}
}
由于base是void*类型的指针,但是void*类型不能进行后续的操作,所以我们强制类型转换成char* ,由于char类型元素占一个字节,当我们要找到下一个元素进行比较时,就得用我们所需元素的字节数width*j来找到下一个元素。
这时我们就通过函数指针来调用cmp函数,这样的好处时我们可以定义多个cmp函数,例如cmp_int,cmp_float,cmp_char,就可以实现多种类型数据的排序。
//实现一个比较整型的函数
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
最后再实现交换函数Swap,由于参数是char*,解引用后为char类型,所以进行一个字节一个字节交换,在数据与数据交换之后,buf1和buf2向后移动,交换width次,如果是整型,在32位平台下是四个字节,一次交换一个字节的内容,四个字节就交换四次。
void Swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}