目录
一 . 认识qsort函数
💛📑📖💛👇👇👇
qsort函数是C语言库里的排序函数,能够实现多种数据类型的排序,其头文件包含在<stdlib.h>。参数解释如下
void qsort( void *base, size_t num, size_t width, int (*cmp )(const void *elem1, const void *elem2 ) );
1.base 待排序数组的初始地址;base的类型为void * 是为了接收各种类型的数据,从而实现对多种类型的数据排序
2.num 数组中元素个数
3.width 数组中一个元素占几个字节大小
4.cmp 比较函数---这是qsort函数实现的精髓,通过自己实现比较函数的编写来规定qsort函数按照怎样的方式去排序(不同数据类型不同,升序降序也不同)
❗️❗️cmp函数的返回值有明确规定
💥👊✌️废话不多说,直接上手实现怎么使用qsort函数👏📖✌️
二. qsort 函数的实现
1️⃣整型数组排序
#include<stdio.h>
int int_cmp(const void*e1,const void*e2)//自己编写cmp函数规定qsort函数按照什么样的规则去排序,
{
return (*(int*)e1-*(int*)e2); //先把e1,e2强制转化成int* 然后解引用后相减如果*e1>*e2 就返回大于零的值,小于就返回小于0的值。满足cmp的返回规则
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
{
printf( "%d ", arr[i]);
}
printf("\n");
return 0;
}
代码运行结果👇👇👇
2️⃣浮点型数组的实现
注意!!!比较函数中返回值为int类型,在比较浮点数时强制转化成int类型时可能会出错,例如 9.9-9.8=0.2 (int)(0.2)=0;导致判断出错,应注意此时和int类型排序的不同
#include<stdio.h>
int float_cmp(const void*e1,const void*e2)
{
if((*(float*)e1-*(float*)e2) > 0.00000)
return 1;
if(((*(float*)e1-*(float*)e2)) == 0.00000)
return 0;
else
return -1;
}
int main()
{
float arr[] = { 1.561, 0.456, 0.789, 7.123, 9.598, 2.598, 4.458, 6.123, 8.458, 0.000 };
int i = 0;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (arr[0]), float_cmp);
for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
{
printf( "%f ", arr[i]);
}
printf("\n");
return 0;
}
运行结果如下:👇👇👇
3️⃣字符数组排序
#include<stdio.h>
int char_cmp(const void*e1,const void*e2)
{
return(*(char*)e1 - *(char*)e2);//比较字符的大小实际是比较字符ASII码的大小,所以直接返回两个字符相减的值。
}
int main()
{
char arr[] = "rjc321"
int i = 0;
int sz = strlen(arr);
qsort(arr, sz, sizeof (arr[0]), char_cmp);
for (i = 0; i < sz; i++)
{
printf( "%c ", arr[i]);
}
printf("\n");
return 0;
}
运行结果如下:👇👇👇
4️⃣结构体数组排序
因为我们定义的结构体里面含有不同种数据类型,所以在进行结构体排序时,比较函数应根据要比较结构体中待比较元素的数据类型来确定
struct Stu
{
char name[20];
int age;
double score;
};
int cmp_stu_by_age(const void* e1, const void*e2)//比较结构体中的年龄,实际就是整型数组的比较
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int cmp_stu_by_name(const void* e1, const void* e2)//比较结构体中的名字,实际是字符串的比较,使用strcmp函数
{
return strcmp( ((struct Stu*)e1)->name , ((struct Stu*)e2)->name);
}
//使用strcmp函数比较字符串时实际是一个一个字符的比较,也就是字符串中各个字符ASII码值的比较
//abc ad----> 返回值<0
//cba abc----> 返回值>0
//abc abc-----> 返回值=0
void test()
{
struct Stu arr[3] = { {"zhangsan", 20, 55.5},{"lisi", 30, 88.0},{"wangwu", 10, 90.0}};
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);//按年龄
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);//按名字
}
按名字排序👇👇👇
按年龄排序👇👇👇
5️⃣指针数组排序
!!!指针数组是数组,数组里面存放的是指针,如下图所示数组里面存放的分别是指向各个字符串的指针,类型为char*,str指向的是该指针数组的指针,所以str的类型是char**,所以强制类型转化时应该把str强制转化为char**,然后解引用才能找到char*的arr 进行比较
int cmp_pchar(const void* e1, const void* e2)
{
return strcmp(*(char**)e1, *(char**)e2);
}
void test()
{
char* arr1 = "cbc";
char* arr2 = "acd";
char* arr3 = "bde";
char* str[3] = { arr1,arr2,arr3 };
qsort(str, sizeof(str) / sizeof(str[0]), sizeof(str[0]),cmp_pchar);
int i = 0;
for (i = 0; i < sizeof(str) /sizeof( str[0]); i++)
{
printf("%s\n", str[i]);
}
}
int main()
{
test();
return 0;
}
运行结果如下:👇👇👇
6️⃣降序排列
在之前的基础上实现降序排列,只需要与cmp返回值的逻辑相反即可,e1所指向的元素大于e2所指向的元素时cmp返回大于0的数,
要实现逻辑相反只需要把e1-e2改成e2-e1即可。
#include<stdio.h>
int int_cmp(const void*e1,const void*e2)
{
return (*(int*)e2-*(int*)e1);
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
{
printf( "%d ", arr[i]);
}
printf("\n");
return 0;
}
运行结果如下:👇👇👇
三 . 模拟实现qsort(冒泡排序升级版)
1️⃣冒泡排序
😍👇✌️🔥先上动图
整体思想:一共排n-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++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
2️⃣升级版
运用冒泡排序思想和qsort排序理论实现冒泡排序升级,可以排序多种数据类型的数组
细节1.将base强制转化为char*指针,这样处理的话char*+1 跳过一个字节,+width跳过width个字节,相当于跳过一个元素,这样不管是什么类型的元素进行比较时,只要给出一个元素占多少个字节(width)我们就可以利用+width来指向下一个元素,粒度达到最细。适用于多种类型的数据.
if(cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)
j=0时比较的是第一和第二个元素,j=1时比较的是第2和第三个元素
细节2;swap交换函数 当cmp>0时要交换e1,e2所指向元素的内容 这里的实现方法是通过传地址,然后一个字节一个字节的交换.
!数据在内存中是以小端存储的方式存储的。
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 print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int cmp_stu_by_age(const void* e1, const void*e2)//比较结构体中的年龄,实际就是整型数组的比较
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int cmp_stu_by_name(const void* e1, const void* e2)//比较结构体中的名字,实际是字符串的比较,使用strcmp函数
{
return strcmp( ((struct Stu*)e1)->name , ((struct Stu*)e2)->name);
}
int int_cmp(const void*e1,const void*e2)//自己编写cmp函数规定qsort函数按照什么样的规则去排序,
{
return (*(int*)e1-*(int*)e2); //先把e1,e2强制转化成int* 然后解引用后相减如果*e1>*e2 就返回大于零的值,小于就返回小于0的值。满足cmp的返回规则
}
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 bubble_sort(void* base, int num, int width, int (*cmp)(const void*e1, const void*e2))
{
int i = 0;
for (i = 0; i < num - 1; i++)
{
int j = 0;
for (j = 0; j < num - 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 test1()
{
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);
print_arr(arr, sz);
}
void test2()
{
struct Stu arr[3] = { {"zhangsan", 20, 55.5},{"lisi", 30, 88.0},{"wangwu", 10, 90.0} };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
//bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
😄😄😄✌️😄✌️😄结语
由于自己还是萌新小白阶段,功力有限,如果本篇文章所包含的知识点存在我理解错误或者使用不当的地方,欢迎各位大佬斧正。
😋😴😴