一:分析自己写出的排序函数的缺点
1.先写出一个极为简单的排序函数(我们先不关心实现排序的算法好坏,只是实现排序功能)
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++)
{
int temp = 0;
if (arr[j] < arr[j + 1])
{
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
int main()
{
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
2.分析自己写出的函数
缺陷一:
void bubble_sort(int arr[], int sz)
分析函数参数,不难发现,我们传入的数组元素类型只能为int,一旦我们如下定义结构体数组:
struct stu {
char name[10];
short age;
};
int main()
{
struct stu s[3] = { {"张三",20},{"李四",18},{"王五",28} };
pubble_sort(s,sz);//error
}
该数组名s无法通过buttle_sort函数传参。
缺陷二:
即使我们不考虑函数传参问题,我们不难发现,同样定义上面的结构体,我们无法像整数那样直接比较大小来排序,结构体是一个整体,它肯定不能简单的比较a[j]和a[j+1]的大小,那么它该以怎样的方式来排序呢?
让我们来看看c库函数qsort是如何解决这个问题的。
二:分析qsort函数
从官网找到库函数qsort
void qsort (void* base, size_t num, size_t size,
int (*compar)(const void*,const void*));
从官方文档的介绍中,我们可以清楚的认识到qsort函数各个参数的意义。
(1)void* base参数
void*base定义了一个无类型的指针,它可以被任何类型的指针赋值。实际上base指针就是我们要传进去的数组名(同时也是数组首元素的地址)。
从这里我们可以明显看出设置为void类型的好处:
在我们写的函数中,参数类型是int*,这意味着我们只能传入int型数组的指针,而设置为void
后,即使我们要排序的数组元素为浮点数或结构体等等,我们也可以将其传参给void*,然后强制
转化为我们想要转化的类型。
(2)num参数
num参数实际上就是数组中元素的个数,用sizeof(arr)/sizeof(arr[0])即可。
(3)size参数
size是指数组中每个元素所占的字节大小,用sizeof(arr[0])即可。
(4)int (*compar)(const void*,const void*)参数
这是一个函数指针,指向一个compar函数,这个函数就是用来定义如何来比较大小的。这个函数有两个参数,都是const void*类型。实际上,由于每个人要进行排序的数组类型不同,这个函数是由我们自己来定义的。
但是官方对于该函数的返回值进行了规定,当第一个元素小于第二个元素,返回一个<0的数;当第一个元素等于第二个元素,返回0;当大于时,返回一个>0的数。
我们通过compar函数来定义如何比较两个参数的大小,然后调用qsort函数,通过我们给出的比较两个参数大小的方法,对整个数组进行排序。
让我们直接看个例子
三:用qsort函数实现排序
(1)实现整形数组排序
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;//强制转化为int*类型,之后解引用,再相减
}
void test1()
{
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ",arr[i]);
}
}
int main()
{
text1();
return 0;
}
在这里我们自己定义了整形数组的比较大小函数cmp_int
(2)实现浮点型数组排序
int cmp_float(const void* e1, const void* e2)
{
return (int)(*(float*)e1 - *(float*)e2) ;
}
void test2()
{
float arr[] = { 8.0,7.0,6.0,5.0,4.0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_float);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%f ", arr[i]);
}
}
int main()
{
test2();
return 0;
}
(3)实现结构体数组排序
1.按age排序:
struct stu {
char name[10];
short age;
};
int cmp_stu_by_age(const void* e1, const void* e2)
{
return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
}
void test3()
{
struct stu arr[3] = { {"张三",20},{"李四",18},{"王五",28} };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}
int main()
{
test3();
return 0;
}
通过监视窗口可以清楚看到排序变化:
2.按名字首字母排序:
struct stu {char name[10];
short age;
};
int cmp_stu_by_name(const void* e1, const void* e2)
{
return strcmp(((struct stu*)e1)->name,((struct stu*)e2)->name);//调用库函数strcmp来比较字符串大小
}
void test4()
{
struct stu arr[3] = { {"张三",20},{"李四",18},{"王五",28} };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
int main()
{
test4();
return 0;
}
通过监视窗口看到数组元素序列变化如下:
四:将自己写出的bubble_sort写成qsort函数的形式
(1)bubble_sort函数主体
//冒泡排序,能排序所有类型数组
void bubble_sort(void* arr, int sz, int width, int(*cmp)(const void* e1, const void* e2))
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
//一趟冒泡排序
int j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
swap((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
}
}
}
(2)swap交换函数
//交换函数
void swap(char* e1, char* e2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char temp = *e1;
*e1 = *e2;
*e2 = temp;
e1++;
e2++;
}
}
(3)自定义cmp函数
这个函数我们之前已经写过了,就不再重复
(4)测试函数,测试整形,浮点型和结构体类型
这三个函数也已经写过,不再重复
五:总代码
#include<stdio.h>
#include<string.h>
//交换函数
void swap(char* e1, char* e2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char temp = *e1;
*e1 = *e2;
*e2 = temp;
e1++;
e2++;
}
}
//冒泡排序,能排序所有类型数组
void bubble_sort(void* arr, int sz, int width, int(*cmp)(const void* e1, const void* e2))
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
//一趟冒泡排序
int j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
swap((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
}
}
}
//自定义比较函数
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
void test1()
{
int arr[10] = { 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 cmp_float(const void* e1, const void* e2)
{
return (int)(*(float*)e1 - *(float*)e2);
}
void test2()
{
float arr[] = { 8.0,7.0,6.0,5.0,4.0 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_float);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%f ", arr[i]);
}
}
struct stu {
char name[10];
short age;
};
int cmp_stu_by_name(const void* e1, const void* e2)
{
return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}
void test3()
{
struct stu arr[3] = { {"张三",20},{"李四",18},{"王五",28} };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
int main()
{
test1();
printf("\n");
test2();
printf("\n");
test3();
return 0;
}