目录

1. 使用qsort实现快速排序

2. 模拟实现qsort


使用qsort实现快速排序

C语言中比较常见的排序算法有两种:一是冒泡排序法,这种方法在之前的文章中有详细介绍过,这里就不做过多赘述。但是因为冒泡排序法只能对整型进行排序,显得比较局限,所以就引出了第二种方法——qsort快速排序法,接下来会对qsort做一个基本的了解。

qsort用前须知:qsort是用快速排序的方法实现,其是C语言中的个库函数,所以使用之前要引头文件<stdlib.h>。qsort相较于冒泡排序的算法就比较灵活,它不仅可以对整型数组进行排序,还可以对字符数组、浮点型数组、结构体数组等进行排序。

首先,简单介绍一个qsort的基本用法以及传入参数等,具体的实现通过下面的例子渗透了解。

注:使用的参考资料来自cplusplus网站,以下为该网站网址(里面会对C/C++中库函数做出详细介绍)cplusplus.com - The C++ Resources Networkhttp://www.cplusplus.com/

接下来,先以qsort排序整型数组作为引入。

#include <stdio.h>
#include <stdlib.h>int cmp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}void print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}void test1()
{int arr[] = { 3,4,6,1,9,8,0,2,5,7 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);
}int main()
{test1();return 0;
}

上面使用qsort库函数,短短的几行代码就可以实现整型数组排序,确实比冒泡排序简单了不少。这里再次强调,qsort函数的4个参数分别为数组首元素地址、数组元素个数、每个元素大小、指向两个比较元素的函数指针(这几个参数位置不能互换)。其中,上面代码中比较灵活的是cmp_int函数,因为在函数指针那里就已经规定了指向函数的返回值应该是大于等于或者小于0了,cmp_int函数中巧妙地将两个地址进行解引用操作再相减并返回,其返回值正好与规定的返回值相符。

因为上面频繁出现void*。这里,对void*做一个整理,了解void*是什么、用法以及一些注意事项等。

1. void* 是一种无具体类型的指针。
2. void* 的指针变量可以存放任意类型的地址。
3. 但是,void* 的指针不能直接进行解引用操作,也不能直接进行+-整型。
4. 所以,如果要对void* 进行上面的两种操作,需要先强制类型转换变成自己想要的类型再进行解引用操作。

通过上面qsort排序整型数组的基础,接着,就是qsort排序结构体数据了(先上代码再解释):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>struct Stu
{char name[20];int age;float score;
};int cmp_stu_by_score(const void* e1, const void* e2)
{if (((struct Stu*)e1)->score > ((struct Stu*)e2)->score){return 1;}else if (((struct Stu*)e1)->score < ((struct Stu*)e2)->score){return -1;}else{return 0;}
}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)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}void print_stu(struct Stu arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%s %d %.2f\n", arr[i].name, arr[i].age, arr[i].score);}printf("\n");
}void test2()
{struct Stu arr[] = { {"张三",10,85.5f},{"李四",22,72.5f},{"王五",14,90.0f} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_score);printf("按成绩排序\n");print_stu(arr, sz);
}void test3()
{struct Stu arr[] = { {"张三",10,85.5f},{"李四",22,72.5f},{"王五",14,90.0f} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);printf("按年龄排序\n");print_stu(arr, sz);
}void test4()
{struct Stu arr[] = { {"张三",10,85.5f},{"李四",22,72.5f},{"王五",14,90.0f} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);printf("按名字排序\n");print_stu(arr, sz);
}int main()
{test2();   //按成绩排序test3();   //按年龄排序test4();   //按名字排序return 0;
}

由上面代码不难看出,表面是对结构体数据进行排序,实际上是分别对浮点型、整型、字符串类型进行排序。所以也就印证了前面的说法:qsort是实现快速排序的方法,可排序整型数组、字符数组、浮点型数组、结构体数组等。

要理解并写出这样的代码需要对函数指针、数组、结构体、结构体指针等掌握地比较扎实。其中,此段代码值得讲的是:

1. 因为指向的这个函数的返回类型的int类型,所以在比较浮点数的时候,不能像整型那样直接返回两个整数相减的值,对此,只能老老实实地使用判断语句分成3部分进行返回。

2. 对于比较字符类型,也可以像浮点数那样分情况返回,但是,有一种更加简洁的做法,就是调用strcmp函数,因为strcmp是专门用来比较字符串的。在cplusplus中搜索这个函数也不难发现strcmp的返回值的形式与qsort返回值的形式如出一辙地相似,所以排序字符类型,就可以在指向的函数返回内容中直接使用strcmp。

模拟实现qsort

在宏观了解完qsort的用法等内容后,第二部分就是要深入理解qsort,也就是要来模拟实现qsort。

模拟实现qsort就需要用到上篇文章所讲到的回调函数,具体的代码如下(代码实现解释在后面):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>struct Stu
{char name[20];int age;float score;
};int cmp_stu_by_name(const void* e1, const void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}int cmp_stu_by_age(const void* e1, const void* e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}int cmp_stu_by_score(const void* e1, const void* e2)
{if (((struct Stu*)e1)->score > ((struct Stu*)e2)->score){return 1;}else if (((struct Stu*)e1)->score < ((struct Stu*)e2)->score){return -1;}else{return 0;}
}void Swap(char* buf1, char* buf2, int width)
{int i = 0;for (i = 0; i < width; i++){char ret = *buf1;*buf1 = *buf2;*buf2 = ret;buf1++;buf2++;}
}int My_qsort(void*base,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; 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 print_stu(struct Stu arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%s %d %.2f\n", arr[i].name, arr[i].age, arr[i].score);}printf("\n");
}void test5()
{struct Stu arr[] = { {"张三",10,85.5f},{"李四",22,72.5f},{"王五",14,90.0f} };int sz = sizeof(arr) / sizeof(arr[0]);My_qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);   //按名字排序printf("按名字排序\n");print_stu(arr, sz);My_qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);   //按年龄排序printf("按年龄排序\n");print_stu(arr, sz);My_qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_score);   //按成绩排序printf("按成绩排序\n");print_stu(arr, sz);
}int main()
{test5();return 0;
}

下面详细解释一下本代码是如何模拟实现qsort的:

先分别按名字、年龄、成绩传入自定义函数My_qsort中,由这个封装的函数来分别调用不同的函数,其中,My_qsort中的最后一个参数为函数指针,它不由本函数实现,而是先传到自定义函数My_qsort中再在此函数中调用,这就是上篇代码中说到的回调函数,这样写可以大量地减少代码的行数。接下来,就可以把封装出的My_qsort函数通过最后那个参数分别指向按不同类型排序的函数(这些函数与之前的函数一样)。其实,模拟实现qsort最大的难点就是如何理解这些传进去的参数,从而进一步理解My_qsort的原理,传入的参数中最难理解的又是width(也就是数组中一个元素的大小),那么,为什么要传入这个参数呢?

因为前面传入的是数组首元素地址,但是又不知道其是什么类型(int、char、float等),不同类型之间元素的大小是不一样的,所以,这里就会想到将元素的大小也传过去,这样就可以对不同类型内存中的虚拟地址进行判断,同时也便于后面对两个元素进行交换排序等。所以,函数指针中的参数就应该先强制类型转换为char*类型,再根据该类型每个元素的宽度进行对应的操作(这里其实到最后传的也是首元素地址,只是不知道类型,只能通过一个一个来找到第二个数),然后判断返回值是否大于0,如果大于0则说明前一个元素比后一个元素大,需要进行元素交换。交换过程也是需要传元素宽度过去,因为交换的元素也是不知道是什么类型,传首元素地址过去也是跟前面一样需要强制类型转换成char*类型,在传入的函数中以字节为单位进行交换,使用循环,直到全部交换完为止。

以上就是模拟实现qsort的全部过程,通过模拟实现qsort的方法也是跟冒泡排序的思想十分类似的。其实,C语言在创作这个库函数时候的思想就是运用冒泡排序的思想的,所以,就算是qsort实现快速排序的方法,其底层原理也是冒泡排序,也是对冒泡排序进行的一个推广,从只能排序整型到可以排序任何类型,变得更加实用。

C语言:深入理解排序算法相关推荐

  1. c语言排序算法实际案例,[C语言] 部分经典排序算法详解(有图解)

    目录 1.内容概括 2.主要算法 3.技术的具体应用 4.算法实际应用 5.总结 0.前言 在上一篇文章<[C语言] 数组的实际应用三则>中我们提到了数组的一些基础知识,并通过三个实际例子 ...

  2. 用c语言编写插入排序算法,C语言实现常用排序算法——插入排序

    插入排序是最基础的排序算法,原理: 首先1个元素肯定是有序的,所以插入排序从第二个元素开始遍历: 内循环首先请求一个空间保存待插入元素,从当前元素向数组起始位置反向遍历: 当发现有大于待插入元素的元素 ...

  3. 他山之石,可以攻玉--从伪代码的角度来理解排序算法

    在学习各种排序算法的过程中,网上看到的参考资料大部分都是类似于"xxx语言中排序算法的实现",然后直接给出某种编程语言中各种排序算法的代码实现.这类的参考资料,容易让初学者纠结于所 ...

  4. c语言sort_C语言十大排序算法,让老师对你刮目相看的技巧

    排序算法作为数据结构的重要部分,系统地学习一下是很有必要的. 十种常见排序算法可以分为两大类: 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时 ...

  5. c语言实现各种排序,c语言实现各种排序算法

    以下是我用c语言实现的各种排序算法#pragma once; #define MAXSIZE 20000 typedef int KeyType; typedef char Infomation; t ...

  6. 【C语言】八大排序算法

    文章目录 1.排序的概念及其应用 1.1排序的概念 1.2排序的应用 1.3常见的排序算法 1.4时间性能的测试(测试排序算法的好坏) 2.常见排序算法的实现 2.1直接插入排序 2.2希尔排序 2. ...

  7. C语言实现八大排序算法详解及其性能之间的

    概述 排序是数据结构中的重要一节,也是算法的重要组成部分.主要分为内部排序以及外部排序,今天我们讲内部排序,也就是八大排序. 插入排序 直接插入排序 算法思想 算法图解 算法分析 算法实现 希尔排序 ...

  8. C语言之选择排序算法

    C语言学习交流群:648422161.志同道合的小伙伴可以进群交流哦! 对于选择排序,咱们首先理解排序的思想.给定一个数组,这种思想首先假定数组的首元素为最大或者最小的.此时就要利用3个变量表示元素的 ...

  9. c语言的八大排序算法,程序员的内功:C语言八大排序算法

    四 一.冒泡排序 冒泡排序算法的运作如下: ●比较相邻的元素.如果第一个比第二个大,就交换他们两个. ●对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.这步做完后,最后的元素会是最大的数. ...

最新文章

  1. 函数防抖和函数节流的最简单解释
  2. linux有关网络服务的接口,linux系统有关网络服务接口定义是哪个?
  3. 2021年11月Python小屋编程比赛获奖名单
  4. ios中navigationItem的titleView如何居中
  5. 笔记本交还公司了,伴随了我两年的家伙。
  6. homework-06
  7. 翻译 Real-Time Global Localization of Robotic Cars in Lane Level via Lane Marking Detection and Shape
  8. kali Linux外网渗透控制Android安卓手机系统。
  9. C++控制输出对齐---setw()函数
  10. 「 数学模型 」“三角函数化简公式”小结
  11. zzulioj1138: C语言合法标识符
  12. 博客转移至 http://sunhs.me
  13. 微信小程序图片组件,ios不显示,安卓正常
  14. PS PhotoShop CS5 CS6 序列号 安装
  15. 红外平行光管ZEMAX光学设计/SOLIDWORKS
  16. 小菜鸟之JAVA面试题库1
  17. 目标跟踪算法--Camshift 和Meanshift
  18. about wParam and lParam
  19. “===”、“==” 的区别
  20. 淘宝商品评价api接口,淘宝评论视频API接口,淘宝评论API接口(app、h5端)

热门文章

  1. 立等可取:工具定制让Oracle优化变得更简单快捷
  2. 无人机360°VR全景图制作方法
  3. 腾讯Tinker 热修复 Andriod studio 3.0 配置和集成(二)多渠道打包和补丁发布
  4. 摆动特效 html,html5 css3催眠怀表摇摆动画特效
  5. 企业网管软件之SOLARWINDS实战-制作拓扑图
  6. [经验教程]2022年淘宝天猫618购物清单指南:天猫淘宝618购物节什么时候开始?
  7. 计算机视觉库/人脸识别开源软件
  8. spring mvc前端页面中文乱码问题解决思路
  9. 安卓h5混合开发照片上传的问题
  10. 07- 梯度下降优化(Lasso/Ridge/ElasticNet) (数据处理+算法)