引入
我们之前实现过一个冒泡排序函数,将传入的整型数组中的每个元素从小到大排序(升序)。冒泡排序的大致内容如下:
#include<stdio.h>
void bubble(int arr[], int sz)
{
for (int i = 0;i < sz - 1;i++)
{
for (int j = 0;j < sz - i - 1;j++)
{
if (arr[j] > arr[j + 1])
{
int a = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = a;
}
}
}
}
void print(int arr[], int sz)
{
for (int i = 0;i < sz;i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[10] = { 3,1,5,4,7,9,8,2,6,0, };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble(arr, sz);
print(arr,sz);
return 0;
}
其中print也是自制的函数,用于打印整型数组的内容。
但是就仅仅是冒泡排序是不够的,因为此排序不能解决所有的排序问题。比如说我要对字符数组排序,对结构体进行排序,此排序都是做不到的。但是冒泡排序有我们可以借鉴的地方。冒泡排序的二重循环是可以应对任何排序问题的,都是一次大循环将最大的数送到最后面。
for (int i = 0;i < sz - 1;i++)
{
for (int j = 0;j < sz - i - 1;j++)
{
}
}
但是循环内部的代码就无法使用。比如我要排序字符数组,就不可以用“>”"<""="">=""<="等符号,之前学过比较两个字符串的大小用的是strcmp函数,大于就返回正值,小于返回负值,等于返回0。所以具体的“比较”这个步骤不同的排序所要求的代码时不同的。
既然处理不同的排序问题,存在相同的部分,也有不同的步骤,那么我们是不是可以设计一个函数,这个函数用于处理一切的排序问题,它将两重循环,也就是相同的部分包装起来,至于不同的部分则需要我们给它传参数(函数指针),告诉它用什么样的方式排列。这就是我们今天讨论的中心,qsort函数(库函数)。
qsort函数
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))
这是qsort的传参需要。其中void*base传入的是首元素的地址,size_t nitems传入的是元素的个数(size_t 是无符号整型类型),size_t size传入的是每个元素的大小,int (*compar)(const void *, const void*)是函数指针,它规定了此函数的返回类型与传参要求,是告诉qsort函数如何排序(当然这个函数要我们自己实现)。下面我们举几个例子。
1.字符指针的排序
#include<stdio.h>
#include<stdlib.h>//qsort函数需要的头文件
#include<string.h>//strcmp函数需要的头文件
void print(char arr[], int sz)//打印字符数组的函数
{
for (int i = 0;i < sz;i++)
{
printf("%c ", arr[i]);
}
}
int charfunc(const void*a, const void*b)
{
//这里用到了strcmp函数,此函数要传入两个字符指针。
int c = strcmp((char*)a, (char*)b);
return c;
}
int main()
{
char arr[7] = "bcdafe";
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sizeof(arr) / sizeof(arr[0]), 1, charfunc);
print(arr, sz);
return 0;
}
2.结构体的排序 (自定义学生类)
1.按年龄排序
struct stu
{
char name[20];
int age;
};
int agefunc(const void*a, const void*b)
{
int c = ((struct stu*)a)->age - ((struct stu*)b)->age;
return c;
}
int main()
{
struct stu s[] = { {"wang",18},{"li",25},{"zhong",20} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), agefunc);
return 0;
}
2.按名字排序
struct stu
{
char name[20];
int age;
};
int namefunc(const void* a, const void* b)
{
int c = strcmp((char*)a, (char*)b);
return c;
}
int main()
{
struct stu s[] = { {"wang",18},{"li",25},{"zhong",20} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), namefunc);
return 0;
}
自制qsort函数
我们接下来尝试自制my_sort函数。当然库函数qsort是用快速排序的方法实现的,我们这里就用冒泡排序的方法模拟实现。
void swap(void* a, void* b, int size)//实现交换的函数
{
for (int i = 0;i < size;i++)
{
char c = *((char*)a + i);//特别注意,中间变量c的类型用char
*((char*)a + i) = *((char*)b + i);
*((char*)b + i) = c;
}
}
//自制qsort函数my_sort
void my_sort(void* base, int sz, int size, int(*cmp)(const void(*a), const void(*b)))
{
for (int i = 0;i < sz;i++)
{
for (int j = 0;j < i;j++)
{
if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
{
swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
}
}
}
}
//定义结构体(学生)
struct stu
{
char name[20];
int age;
};
int agefunc(const void*a, const void*b)//按年龄排序的函数
{
int c = ((struct stu*)a)->age - ((struct stu*)b)->age;
return c;
}
int namefunc(const void* a, const void* b)//按名字排序的函数
{
int c = strcmp((char*)a, (char*)b);
return c;
}
int main()
{
struct stu s[] = { {"wang",18},{"li",25},{"zhong",20} };
int sz = sizeof(s) / sizeof(s[0]);
my_sort(s, sz, sizeof(s[0]), namefunc);//检验自制my_sort函数
return 0;
}