在日常生活中,经常涉及到对数据的处理和分析。有时我们需要对一组杂乱无章的数据进行排序,这时候就需要用到排序算法。一般的对于整型数据的处理,我们可以用冒泡排序:
冒泡排序的应用举例如下:
#include
#include
void test0(int arr[], int sz)//冒泡排序基本算法
{
int i, j;
//进行冒泡排序的趟数
for (i = 0; i < sz; ++i)
{ //每趟冒泡排序的对数
for (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;
}
}
}
}
int main()
{
int arr[] = { 5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("before : ");
for (int i = 0; i < sz; ++i)
{
printf("%d ", arr[i]);
}
test0(arr, sz);
printf("\n");
printf("after: ");
for (int i = 0; i < sz; ++i)
{
printf("%d ", arr[i]);
}
return 0;
}
输出结果如下;
然而生活中我们碰到的数据远不止是整型,有时是姓名,浮点小数(利率)以及甚至是一个复杂的对象,因此上述这种冒泡排序的弊端就显现出来了(只能排序整数)。事实上,C语言提供了一个为我们排序复杂对象的函数-qsort()。
我们先来看qsort()的函数原型:
void qsort (void* base, size_t num, size_t size,
int (*compar)(const void*,const void*));
其中size_t是一个无符号整型的别名,compar是一个函数指针(存放函数地址)
下面我们来使用qsort()来排序浮点数:
#include
#include//包含qsort()的原型及定义
//参数是函数指针,说明该指针能够接受形参类型相同,返回类型也相同的函数
int cmp_float(const float * a,const float* b)//比较浮点数的函数(使得能被收)
{
if (*a == *b)
return 0;
else if (*a > *b)
return 1;
else
return -1;
}
int main()
{
float arr[] = { 5.0,4.0,3.0,2.0,1.0 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("before: ");
for (int i = 0; i < sz; ++i)
{
printf("%lf ", arr[i]);
}
qsort(arr,sz, sizeof(arr[0]), cmp_float);
printf("\n");
printf("after: ");
for (int i = 0; i < sz; ++i)
{
printf("%lf ", arr[i]);
}
return 0;
}
运行结果如下:
讲完了怎么使用,不妨我们尝试模拟实现这一个函数(核心算法是冒泡排序)
我们注意到qsort()函数的第一个参数的类型是一个无类型的指针(不是空指针)(意味着我们可以用强制类型转换成我们所要的指针),同时我们还要知道元素的个数和元素的大小,最重要的是我们要针对不同的数据类型设计不同的比较方法(把比较方法抽离出来)
核心部分的代码:
void Myqsort(void* base, int sz, int width, int(*cmp)(void* e1, void* e2))
{
int i = 0
;
//比较趟数
for (i = 0; i < sz; ++i)
{ //每趟比较对数
for (int j = 0; j < sz - i - 1; ++j)
{//强制成字符指针,是因为字符型指针每解引用一次都会访问一个字节。
//乘以宽度就是跳过宽度个字节,再乘以变量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 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;
}
}
抽离的比较方法:
//比较整数
int cmp_int(void* e1, void* e2)
{
return *(int*)(e1)-*(int*)(e2);
}
//比较浮点数
int cmp_float(void* e1, void* e2)
{
if (*(float*)e1 == *(float*)e2) return 0;
else if (*(float*)e1 > *(float*)e2) return 1;
else return -1;
}
//比较学生结构体的年龄
int cmp_stu_age(void* e1, void* e2)
{
return ((Stu*)e1)->age - ((Stu*)e2)->age;
}
//比较学生结构体的姓名
int cmp_stu_name(void* e1, void* e2)
{ //箭头的优先级比较高,所以要把强转的结果括号起来
return strcmp(((Stu*)e1)->name, ((Stu*)e2)->name);
}
测试的部分:
//测试整型
void testi()
{
int arr[] = { 9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("before:");
for (int i = 0; i < sz; ++i)
{
printf("%d ", arr[i]);
}
printf("\n");
printf("after: ");
Myqsort(arr, sz, sizeof(arr[0]), cmp_int);
for (int i = 0; i < sz; ++i)
{
printf("%d ", arr[i]);
}
}
//测试浮点型
void testf()
{
float arr[] = { 9.0,8.0,7.0,6.0,5.0,4.0,3.0,2.0,1.0 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("before:");
for (int i = 0; i < sz; ++i)
{
printf("%f ", arr[i]);
}
printf("\n");
printf("after: ");
Myqsort(arr, sz, sizeof(arr[0]), cmp_int);
for (int i = 0; i < sz; ++i)
{
printf("%f ", arr[i]);
}
}
//测试年龄
void testa()
{
Stu s[] = { {"zhansan",16},{"lisi",28}, {"wangwu",22} };
int sz = sizeof(s) / sizeof(s[0]);
printf("before:");
for (int i = 0; i < sz; ++i)
{
printf("%s", s[i].name);
printf("%d ", s[i].age);
}
printf("\n");
printf("after: ");
Myqsort(s, sz, sizeof(s[0]), cmp_stu_age);
for (int i = 0; i < sz; ++i)
{
printf("%s", s[i].name);
printf("%d ", s[i].age);
}
}
//测试姓名
void testn()
{
Stu s[] = { {"zhangsan",16},{"lisi",43}, {"wangwu",22} };
int sz = sizeof(s) / sizeof(s[0]);
printf("before:");
for (int i = 0; i < sz; ++i)
{
printf("%s", s[i].name);
printf("%d ", s[i].age);
}
printf("\n");
printf("after: ");
Myqsort(s, sz, sizeof(s[0]), cmp_stu_name);
for (int i = 0; i < sz; ++i)
{
printf("%s", s[i].name);
printf("%d ", s[i].age);
}
}
主函数部分:
int main()
{
testi();
testf();
testa();
testn();
return 0;
}
测试结果如下:
比较年龄部分:
比较姓名:
完整代码如下:
#include
#include
//参数是函数指针,说明该指针能够接受形参类型相同,返回类型也相同的函数
typedef struct student
{
char name[50];
int age;
}Stu;
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;
}
}
void Myqsort(void* base, int sz, int width, int(*cmp)(void* e1, void* e2))
{
int i = 0
;
//比较趟数
for (i = 0; i < sz; ++i)
{ //每趟比较对数
for (int j = 0; j < sz - i - 1; ++j)
{//强制成字符指针,是因为字符型指针每解引用一次都会访问一个字节。
//乘以宽度就是跳过宽度个字节,再乘以变量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 cmp_int(void* e1, void* e2)
{
return *(int*)(e1)-*(int*)(e2);
}
//比较浮点数
int cmp_float(void* e1, void* e2)
{
if (*(float*)e1 == *(float*)e2) return 0;
else if (*(float*)e1 > *(float*)e2) return 1;
else return -1;
}
//比较学生结构体的年龄
int cmp_stu_age(void* e1, void* e2)
{
return ((Stu*)e1)->age - ((Stu*)e2)->age;
}
//比较学生结构体的姓名
int cmp_stu_name(void* e1, void* e2)
{ //箭头的优先级比较高,所以要把强转的结果括号起来
return strcmp(((Stu*)e1)->name, ((Stu*)e2)->name);
}
//测试整型
void testi()
{
int arr[] = { 9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("before:");
for (int i = 0; i < sz; ++i)
{
printf("%d ", arr[i]);
}
printf("\n");
printf("after: ");
Myqsort(arr, sz, sizeof(arr[0]), cmp_int);
for (int i = 0; i < sz; ++i)
{
printf("%d ", arr[i]);
}
}
//测试浮点型
void testf()
{
float arr[] = { 9.0,8.0,7.0,6.0,5.0,4.0,3.0,2.0,1.0 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("before:");
for (int i = 0; i < sz; ++i)
{
printf("%f ", arr[i]);
}
printf("\n");
printf("after: ");
Myqsort(arr, sz, sizeof(arr[0]), cmp_int);
for (int i = 0; i < sz; ++i)
{
printf("%f ", arr[i]);
}
}
//测试年龄
void testa()
{
Stu s[] = { {"zhansan",16},{"lisi",28}, {"wangwu",22} };
int sz = sizeof(s) / sizeof(s[0]);
printf("before:");
for (int i = 0; i < sz; ++i)
{
printf("%s", s[i].name);
printf("%d ", s[i].age);
}
printf("\n");
printf("after: ");
Myqsort(s, sz, sizeof(s[0]), cmp_stu_age);
for (int i = 0; i < sz; ++i)
{
printf("%s", s[i].name);
printf("%d ", s[i].age);
}
}
//测试姓名
void testn()
{
Stu s[] = { {"zhangsan",16},{"lisi",43}, {"wangwu",22} };
int sz = sizeof(s) / sizeof(s[0]);
printf("before:");
for (int i = 0; i < sz; ++i)
{
printf("%s", s[i].name);
printf("%d ", s[i].age);
}
printf("\n");
printf("after: ");
Myqsort(s, sz, sizeof(s[0]), cmp_stu_name);
for (int i = 0; i < sz; ++i)
{
printf("%s", s[i].name);
printf("%d ", s[i].age);
}
}
int main()
{
testi();
testf();
testa();
testn();
return 0;
}
有不足之处还望指正