• 【c语言】--qsort快速排序【附模拟实现】


    • 它是库函数
    • 它可以排任何类型的数据,如整形数据,字符串数据,结构体数据,而我们一般写的冒泡排序, 选择排序只能排一种数据,有一定的局限性
    • qsort与冒泡排序相似,只是比较不同数据的方法不同,如字符串比较要用strcmp,不能直接用大于或等于比较,因为比较大小方法不同,需自己写个比较大小函数,再传给qsort才行

    二、qsort函数原型

    qsort需引用头文件stdlib.hsearch.h


    qsort函数原型:

    void qsort( void* base, size_t num, size_t size, int (*compar)(const void *e1,const void *e2))

    qsort函数参数:

    • void* base:base中存放待排序数据第一个对象的地址,设为void*是因为它是一个无地址的指针,可以接收任何类型的指针,所以base可以放任何类型的地址【void*不能进行解引用操作且不能对其进行加减整数
    • size-t num:待排序数据的元素个数
    • size-t size:待排序数据中一个元素的大小,单位:字节
    • int (*compar)(const void *e1,const void *e2):一个函数指针,通过传入比较大小的函数(需要自己写)用来比较待排序数据中所传入两个元素的大小,(若排为升序,compar函数返回值>0就交换【第一个数>第二个数】,若排为降序compar函数<0就交换【第一个数<第二个数】,只要==0,不用交换)
    • 本质上qsort函数就像冒泡排序两个元素一点一点往下比的
    • 如果排为升序e1-e2,即第一个元素-第二个元素,排为降序则是e2-e1,即函数第二个元素-第一个元素,因为对于qsort返回>0的数,就会排为升序,排为降序e1-e2和e2-e1正好为相反数那么就会产生升序和降序的效果

    三、qsort函数应用:

    ①、排整形数据【升序】

    1. #include
    2. #include
    3. int cmp_int(const void* e1, const void* e2)
    4. {//返回类型:int 返回这个数>0或<0或=0根据此判断是否排序
    5. //写成void*可以接收任何类型的数据,因为不知道比较的是什么数据
    6. //加上const更具有严谨性,防止e1,e2内容被修改
    7. //因为本题比较的是整形数据,所以比较大小方法如下
    8. return *(int*)e1 - *(int*)e2;
    9. //先转化为int*因为e1和e2均为void*,void*不能解引用,因为void*不确定
    10. //访问几个字节的,*找不到要比较的数据,又因为要排序的数据是整形,所
    11. //以强制类型转换为int*,再解引用能够访问四个字节找到e1和e2的内容
    12. //第一个元素>第一个元素返回>0,反之返回<0,=0不用排序
    13. }
    14. int main()
    15. {
    16. int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
    17. int sz = sizeof(arr) / sizeof(arr[0]);
    18. qsort(arr, sz, sizeof(arr[0]), cmp_int);
    19. //第四个参数解释:函数名即函数地址,而qsort函数第四个参数用函数指针接收的
    20. //cmp_int函数是排什么数据的大小,需要自己写的函数,因为排不同数据方法不同
    21. //所以必须自己写排大小函数,再传给qsort函数地址就可以,原理:回调函数,qsort内部实现
    22. for (int i = 0; i < sz; i++)
    23. {
    24. printf("%d", arr[i]);
    25. }
    26. return 0;
    27. }
    28. //运行结果:0 1 2 3 4 5 6 7 8 9

    ②、排结构体数据【升序】

    1. #include
    2. #include
    3. #include
    4. struct stu
    5. {
    6. char name[20];
    7. int age;
    8. };
    9. int sort_by_age(const void* e1, const void* e2)
    10. {
    11. return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
    12. }
    13. int sort_by_name(const void* e1, const void* e2)
    14. {
    15. return strcmp(((struct stu*)e1)->name,((struct stu*)e2)->name);
    16. }
    17. int main()
    18. {
    19. struct stu s[3] = { {"zhang san",30},{"li si",34},{"wang wu",20} };
    20. int sz = sizeof(s) / sizeof(s[0]);
    21. //按年龄排序
    22. qsort(s, sz, sizeof(s[0]), sort_by_age);
    23. printf("排序后的年龄:");
    24. for (int i = 0; i < sz; i++)
    25. {
    26. printf("%d ", s[i].age);
    27. }
    28. printf("\n");
    29. //按姓名排序
    30. printf("排序后名字:");
    31. qsort(s, sz, sizeof(s[0]), sort_by_name);
    32. for (int i = 0; i < sz; i++)
    33. {
    34. printf("%s ",s[i].name);
    35. //排序名字比的不是长短,比的是ASCii码的值
    36. }
    37. printf("\n");
    38. return 0;
    39. }

    四、qsort函数的模拟实现

    模拟实现qsort,实现一个冒泡排序的通用算法,将arr数组排为升序
    int arr[] = {1,3,5,7,9,2,4,6,8,0};

    注意:我模拟的是qsort函数实现排整型数据升序版本,降序版本还需进一步完善,但是与升序版本基本一样

    ①、如何找到两个元素相比?

    思路与冒泡排序一样,但两个元素相比时,我并不知道怎么找到这两个元素,因为void*是无类型指针,但通过用传入的一个数据的大小,可利用指针访问两个元素,又因为void*不能直接加减运算,故转base为char*char*指针+1等价于往后走一个字节,那排int类型数据,width = 4,(char*)base + 4就可访问下一元素地址,(char*)base + 8就可再访问下一个元素的地址,故(char*)base + j * width(char*)base + (j + 1)* width就可作为比较的前后两个元素的地址,j是冒泡排序内层循环的变量从0开始
     

    ②、如何交换两个元素?

    可以一个一个字节交换交换width次即可(因为width是一个数据的大小)

    完整模拟实现代码如下:

    1. #include
    2. int cmp(const void* e1, const void* e2)
    3. {
    4. return *(int*)e1 - *(int*)e2;
    5. }
    6. void Swap(char* buf1, char* buf2, int width)
    7. {
    8. int i = 0;
    9. for (i = 0; i < width; i++)
    10. {
    11. char tmp = *buf1;
    12. *buf1 = *buf2;
    13. *buf2 = tmp;
    14. buf1++;
    15. buf2++;
    16. }
    17. }
    18. void bubble_sort(void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
    19. {
    20. int i = 0;
    21. for (i = 0; i < sz - 1; i++)//要排sz-1趟,与冒泡排序原理同
    22. {
    23. int j = 0;
    24. for (j = 0; j < sz - 1 - i; j++)
    25. {//两个元素两个元素比较,每次比较完都会少比较一次
    26. if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
    27. {
    28. Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
    29. }
    30. }
    31. }
    32. }
    33. int main()
    34. {
    35. int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
    36. int sz = sizeof(arr) / sizeof(arr[0]);
    37. bubble_sort(arr, sz, sizeof(arr[0]), cmp);
    38. int i = 0;
    39. for (i = 0; i < sz; i++)
    40. {
    41. printf("%d ", arr[i]);
    42. }
    43. return 0;
    44. }
  • 相关阅读:
    场景GIT
    【通信】两层无线 Femtocell 网络上行链路中的最优功率分配附matlab代码
    玩转MyBatis-Plus分页插件一:分页基本使用+方法解释+解析Page对象
    视频|人人能看懂的苹果visionOS空间设计课程
    制作自己的前端组件库并上传到npm上
    Ubuntu上安装Nginx
    jmeter压力测试报告
    淮安美食整理
    opencv项目_人脸识别_LBPH_python
    Linux基础测试题(虚拟机和物理机相ping出现的问题)
  • 原文地址:https://blog.csdn.net/m0_74044018/article/details/133375362