C语言-qsort函数示例解析
一.qsort函数是什么
stdlib.h头文件下的函数
qsort()函数:是八大排序算法中的快速排序,能够排序任意数据类型的数组其中包括整形,浮点型,字符串甚至还有自定义的结构体类型。qsort函数实现对不同元素的排序主要就是通过对compar函数进行定义实现的,对于每一种变量类型的排序,我们都需要重新实现一种compar函数。
qsort函数是根据二分法写的,其时间复杂度为n*log(n)。
快速排序是对冒泡排序的一种改进。它的基本思想是∶通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
排序原理︰
1.首先设定一个分界值,通过该分界值将数组分成左右两部分;
2将大于或等于分界值的数据放到到数组右边,小于分界值的数据放到数组的左边。此时左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值;
3.然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
4.重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左侧和右侧两个部分的数据排完序后,整个数组的排序也就完成了。
图解:

切分原理:
把一个数组切分成两个子数组的基本思想∶
1.找一个基准值,用两个指针分别指向数组的头部和尾部;
2.先从尾部向头部开始搜索一个比基准值小的元素,搜索到即停止,并记录指针的位置;
3.再从头部向尾部开始搜索一个比基准值大的元素,搜索到即停止,并记录指针的位置;
4.交换当前左边指针位置和右边指针位置的元素;
5.重复2,3,4步骤,直到左边指针的值大于右边指针的值停止。
图解:



快速排序和归并排序的区别:
快速排序是另外一种分治的排序算法,它将一个数组分成两个子数组,将两部分独立的排序。快速排序和归并排序是互补的∶归并排序将数组分成两个子数组分别排序,并将有序的子数组归并从而将整个数组排序,而快速排序的方式则是当两个数组都有序时,整个数组自然就有序了。在归并排序中,一个数组被等分为两半,归并调用发生在处理整个数组之前,在快速排序中,切分数组的位置取决于数组的内容,递归调用发生在处理整个数组之后。
快速排序时间复杂分析:
快速排序的一次切分从两头开始交替搜索,直到left和right重合,因此,一次切分算法的时间复杂度为o(n),但整个快速排序的时间复杂度和切分的次数相关。
最优情况∶每一次切分选择的基准数字刚好将当前序列等分。

如果我们把数组的切分看做是一个树,那么上图就是它的最优情况的图示,共切分了]o:n次,所以,最优情况下快速排序的时间复杂度为O(nlogn)
最坏情况∶每一次切分选择的基准数字是当前序列中最大数或者最小数,这使得每次切分都会有一个子组,那么总共就得切分n次,所以,最坏情况下,快速排序的时间复杂度为O(n个2);

平均情况:每一次切公选择的甚准数字不具最大值和最小值,也不是中值,这种情况我们也可以用数学归纳法证明,快速排序的时间复杂度为o(nlogn).由于数学归纳法有很多数学相关的知识,容易使我们混抡,所以这里就不对平均情况的时间复杂度做证明了。
排序的稳定性:
稳定性的定义:
数组arr中有若干元素,其中A元素和B元素相等。并且A元素在B元素前面,如果使用某种排序算法排序后,能够保证A元素依然在B元,素的前面,可以说这个该算法是稳定的。

稳定性的意义:
如果一组数据只需要一次排序,则稳定性一般是没有意义的,如果一组数据需要多次排序,稳定性是有意义的。例如要排序的内容是一组商品对象,第一次排序按照价格由低到高排序,第二次排序按照销量由高到低排序,如果第二次排序使用稳定性算法,就可以使得相同销量的对象依旧保持着价格高低的顺序展现,只有销量不同的对象才需要重新排序。这样既可以保持第一次排序的原有意义,而且可以减少系统开销。
常见排序算法的稳定性:
冒泡排序:
只有当arr[i]>arr[i+1]的时候,才会交换元素的位置,而相等的时候并不交换位置,所以冒泡排序是一种稳定排序算法。
选择排序:
选择排序是给每个位置选择当前元素最小的,,例如有数据{5(1),8,5(2),2,9},第一遍选择到的最小元素为2
所以5(1)会和2进行交换位置,此时5(1)到了5(2)后面,破坏了稳定性,所以选择排序是一种不稳定的排序算法。
插入排序︰
比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相等的,那么把要插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。
希尔排序:
希尔排序是按照不同步长对元素进行插入排序,虽然一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以希尔排序是不稳定的。
归并排序:
归并排序在归并的过程中,只有arrli]arr[i+1]的时候才会交换位置,如果两个元素相等则不会交换位置,所以它并不会破坏稳定性,归并排序是稳定的。
快速排序︰
希尔排序是按照不同步长对元素进行插入排序,虽然一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以希尔排序是不稳定的。
归并排序:
归并排序在归并的过程中,只有arrli]arr[i+1]的时候才会交换位置,如果两个元素相等则不会交换位置,所以它并不会破坏稳定性,归并排序是稳定的。
快速排序︰
快速排序需要一个基准值,在基准值的右侧找一个比基准值小的元素,在基准值的左侧找一个比基准值大的元素,然后交换这两个元素,此时会破坏稳定性,所以快速排序是一种不稳定的算法。
qsort函数参数说明:
void qsort (
void* base, // 需要排序的目标数组名(或者也可以理解成开始排序的地址,可以写&s[i]这样的表达式)
size_t num, // 第二个参数 num 是参与排序的目标数组元素个数
size_t size, // 单个元素的大小(或者目标数组中每一个元素长度),单位是字节,推荐使用sizeof(s[0])这样的表达式 int(*compar)(const void* e1, const void* e2) // 比较函数
);
没有返回值。
其中compar是函数指针,compar指向的是:排序时,用来比较两个元素的函数。需要自己编写。
compar比较函数可以随便写成什么名字,如cmp
典型的cmp的定义是int cmp(const void *a,const void *b);
返回值必须是int,如果不是一定要强制类型转换成int,两个参数的类型必须都是const void *,a, b是随便写的,个人喜好。
返回值:<0(不进行置换),>0(进行置换),0(不进行置换)。
比较函数默认是升序排列,若想降序排列,交换函数实现里的a和b位置即可。
qsort算法不具有稳定性,排序时,相同大小元素相对位置可能会发生改变。
qsort只能针对不要求排序稳定性的场合使用,也即仅对元素排序,元素对应的位置没有意义。
假设是对int排序的话,如果是升序,那是a比b大返回一个正值,小则负值,相等返回0,其他的依次类推。
二.使用qsort排序-多个类型排序示例
#include <stdio.h>
#include <string.h>
#include <stdlib.h>// 注意类型时void* 所以要强制类型转化,还要解引用才是对应的值!!!// 要比较的数据是整形
int cmp_int(const void* a, const void* b)
{return *(int*)a - *(int*)b; // 升序排序 // return *(int *)b - *(int *)a; //降序排序
}// 要比较的数据是浮点型(float)
int cmp_float(const void* a, const void* b)
{//返回值的问题,显然cmp返回的是一个整型,所以避免float返回小数而被丢失(0.2 -0.1 = 0.1 强制类型转化为int后 结果为0),用一个判断返回值。return (*(float*)a > *(float*)b) ? 1 : -1;
}// 要比较的数据是浮点型(double)
int cmp_double(const void* a, const void* b)
{//返回值的问题,显然cmp返回的是一个整型,所以避免double返回小数而被丢失(0.2 -0.1 = 0.1 强制类型转化为int后 结果为0),用一个判断返回值。return (*(double*)a > *(double*)b) ? 1 : -1;
}// 要比较的数据是字符型(char),本质上是比较Ascii值
int cmp_char(const void* a, const void* b)
{return *(char*)a - *(char*)b;
}// 要比较的是字符串的字典顺序
int cmp_str_size(const void* a, const void* b)
{return strcmp((char*)a, (char*)b);
}// 要比较的是字符串的长度
int cmp_str_len(const void* a, const void* b)
{return strlen((char*)a) > strlen((char*)b) ? 1 : -1;
}// 要比较的数据是结构体变量
typedef struct stu
{// char name;int age;double weight;double hight;
}stu;
int cmp_by_weight(const void* a, const void* b)
{return (int)(((stu*)a)->weight - ((stu*)b)->weight);
}int main()
{int i;int arr_len;printf("对int型数组排序\n");int arr_int[] = { 1,2,3,4,5,9,8,7,6 };arr_len = sizeof(arr_int) / sizeof(int);for (i = 0; i < arr_len; i++)printf("%d ", arr_int[i]);printf("\n");qsort(arr_int, arr_len, sizeof(arr_int[0]), cmp_int);for (i = 0; i < arr_len; i++)printf("%d ", arr_int[i]);printf("\n\n");printf("对float型数组排序\n");float arr_float[] = { 1.1f,2.2f,3.3f,4.4f,5.5f,9.9f,8.8f,7.7f,6.6f };arr_len = sizeof(arr_float) / sizeof(float);for (i = 0; i < arr_len; i++)printf("%g ", arr_float[i]);printf("\n");qsort(arr_float, arr_len, sizeof(arr_float[0]), cmp_float);for (i = 0; i < arr_len; i++)printf("%g ", arr_float[i]);printf("\n\n");printf("对double型数组排序\n");double arr_double[] = { 1.1,2.2,3.3,4.4,5.5,9.9,8.8,7.7,6.6 };arr_len = sizeof(arr_double) / sizeof(double);for (i = 0; i < arr_len; i++)printf("%g ", arr_double[i]);printf("\n");qsort(arr_double, arr_len, sizeof(arr_double[0]), cmp_double);for (i = 0; i < arr_len; i++)printf("%g ", arr_double[i]);printf("\n\n");printf("对char型数组排序\n");char arr_char[] = "abcfed";arr_len = sizeof(arr_char) / sizeof(char) - 1; // 因为sizeof把\0也算进去了,所以计算出来的值比字符串本身长度多1for (i = 0; i < arr_len; i++)printf("%c ", arr_char[i]);printf("\n");qsort(arr_char, arr_len, sizeof(arr_char[0]), cmp_char);for (i = 0; i < arr_len; i++)printf("%c ", arr_char[i]);printf("\n\n");printf("对字符串型数组按字典顺序排序-\n(字典序:对于字符串,先按首字符排序,如果首字符相同,再按第二个字符排序,以此类推。如aa, ab, ba, bb, bc就是一个字典序。)\n");
#define STR_SIZE 100char arr_str[][STR_SIZE] = { "a1","ab2","abc3","abc6","ab5" ,"a4" };arr_len = 6;for (i = 0; i < arr_len; i++)printf("%s ", arr_str[i]);printf("\n");qsort(arr_str, arr_len, STR_SIZE, cmp_str_size);for (i = 0; i < arr_len; i++)printf("%s ", arr_str[i]);printf("\n\n");printf("对字符串型数组按长度排序-(是不稳定的排序算法,因此在长度相等的情况下,结果分析比较复杂)\n");for (i = 0; i < arr_len; i++)printf("%s ", arr_str[i]);printf("\n");qsort(arr_str, arr_len, STR_SIZE, cmp_str_len);for (i = 0; i < arr_len; i++)printf("%s ", arr_str[i]);printf("\n\n");printf("对结构体按照变量排序\n");stu arr_class[3] = { {17,185.5,190.8}, {16,160.9,200.7}, {18,120.3,150.5} };arr_len = sizeof(arr_class) / sizeof(arr_class[0]);for (i = 0; i < 3; i++){printf("%.1f ", arr_class[i].weight);}printf("\n");qsort(arr_class, arr_len, sizeof(arr_class[0]), cmp_by_weight);for (i = 0; i < 3; i++){printf("%.1f ", arr_class[i].weight);}printf("\n\n");return 0;
}运行结果:

相关文章:
C语言-qsort函数示例解析
一.qsort函数是什么stdlib.h头文件下的函数qsort()函数:是八大排序算法中的快速排序,能够排序任意数据类型的数组其中包括整形,浮点型,字符串甚至还有自定义的结构体类型。qsort函数实现对不同元素的排序主要就是通过对compar函数…...
一些Linux内核内存性能调优笔记!
前言 在工作生活中,我们时常会遇到一些性能问题:比如手机用久了,在滑动窗口或点击 APP 时会出现页面反应慢、卡顿等情况;比如运行在某台服务器上进程的某些性能指标(影响用户体验的 PCT99 指标等)不达预期…...
【JVM】逃逸分析
开发者都知道,基本上所有对象都是在堆上创建。但是,这里还是没有把话说绝对哈,指的是基本上所有。昨天一位朋友在聊天中,就说了所有对象都在堆中创建,然后被朋友一阵的嘲笑。 开始我们的正文,我们今天来聊聊…...
C51---震动传感器控制LED灯亮灭
1.example #include "reg52.h" sbit led1 P3^7;//原理图中led1指向P3组IO口的P3.7口 sbit vibrate P3^3;//Do接到了P3.3口 void Delay3000ms() //11.0592MHz { unsigned char i, j, k; //_nop_(); i 22; j 3; k 227; do { …...
使用 JaCoCo 生成测试覆盖率报告
0、为什么要生成测试覆盖率报告 在我们实际的工作中,当完成程序的开发后,需要提交给测试人员进行测试,经过测试人员测试后,代码才能上线到生产环境。 有个问题是:怎么能证明程序得到了充分的测试,程序中所…...
windows下neo4j安装及配置,并绘制人物关系图谱
neo4j安装及配置,绘制人物关系图谱 先升级pip,安装py2neo pip install py2neo2021.0.1依赖 jdk1.8, neo4j 3.xx; 或者jdk18,neo4j 4.x,5.x; 官网下载了neo4j4.x,5.x 因为jdk版本原因都不行&am…...
【Spring6】IoC容器之基于XML管理Bean
3、容器:IoC IoC 是 Inversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。 Spring 通过 IoC 容…...
Warshall算法求传递闭包及Python编程的实现
弗洛伊德算法-Floyd(Floyd-Warshall)-求多源最短路径,求传递闭包 Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法, 与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大…...
AcWing第 93 场周赛
4867. 整除数 给定两个整数 n,k,请你找到大于 n 且能被 k 整除的最小整数 x。 输入格式 共一行,包含两个整数 n,k。 输出格式 输出大于 n 且能被 k 整除的最小整数 x。 数据范围 前 4 个测试点满足 1≤n,k≤100。 所有测试点满足 1≤n,k≤109。 …...
计及需求响应的粒子群算法求解风能、光伏、柴油机、储能容量优化配置(Matlab代码实现)
👨🎓个人主页:研学社的博客💥💥💞💞欢迎来到本博客❤️❤️💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密…...
利用Nginx给RStudio-Server配置https
前篇文档,我这边写了安装RStudio-Server的方法。默认是http的访问方式,现在我们需要将其改成https的访问方式。 1、给服务器安装Nginx:参照之前的安装Nginx的方法。 2、创建/usr/local/nginx/ssl目录: mkdir /usr/local/nginx/ss…...
YOLOv7实验记录
这篇博客主要记录博主在做YOLOv7模型训练与测试过程中遇到的一些问题。 首先我们需要明确YOLO模型权重文件与模型文件的使用 其实在github的readme中已经告诉我们使用方法,但我相信有很多像博主一样眼高手低的人可能会犯类似的错误。 训练 首先是训练时的设置&…...
用Python获取史瓦西时空中克氏符的分量
文章目录三维球面坐标史瓦西时空三维球面坐标 Einsteinpy中提供了克氏符模型,可通过ChristoffelSymbols获取。简单起见,先以最直观的三维球面为例,来用Einsteinpy查看其克氏符的表达形式。 三维球面的度规张量可表示为 g001g11r2g22r2sin…...
QML编码约定
QML中的国际化: QML使用以下函数来将字符串标记为可翻译的 qsTr()qsTranslate()qsTrld()QT_TR_NOOP()QT_TRANSLATE_NOOP()QT_TRID_NOOP最常用的还是qsTr() string qsTr(string sourceText, string disambiguation&…...
【Linux】安装Linux操作系统具体步骤
1). 选择创建新的虚拟机 2). 选择"典型"配置 3). 选择"稍后安装操作系统(S)" 4). 选择"Linux"操作系统,"CentOS7 64位"版本 5). 设置虚拟机的名称及系统文件存放路径 6). 设置磁盘容量 7). 自定义硬件信息 8). 启动上述创建的新虚拟机…...
前端ES6异步编程技术——Promise使用
Promise是什么 官方的定义是:Promise是ES6新推出的用于进行异步编程的解决方案,旧方案是单纯使用回调函数来解决的。对于开发人员来说,我们把promise当作一个普通的对象即可,使用它可以用来封装一个异步操作并可以获取其成功/失败…...
Kotlin实现简单的学生信息管理系统
文章目录一、实验内容二、实验步骤1、页面布局2、数据库3、登录活动4、增删改查三、运行演示四、实验总结五、源码下载一、实验内容 根据Android数据存储的内容,综合应用SharedPreferences和SQLite数据库实现一个用户信息管理系统,强化对SharedPreferen…...
413. 等差数列划分
413. 等差数列划分 如果一个数列 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该数列为等差数列。 例如,[1,3,5,7,9]、[7,7,7,7] 和 [3,-1,-5,-9] 都是等差数列。 给你一个整数数组 nums ,返回数组 nums 中所有为等差数…...
设计模式七大原则
一、设计模式概念 1.1 软件设计模式的产生背景 "设计模式"最初并不是出现在软件设计中,而是被用于建筑领域的设计中。 1977年美国著名建筑大师、加利福尼亚大学伯克利分校环境结构中心主任克里斯托夫亚历山大(Christopher Alexander&#x…...
【Mybatis系列】Mybatis常见的分页方法以及源码理解
Mybatis-Plus的selectPage 引入依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency>添加分页插件 Configuration public class My…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
flow_controllers
关键点: 流控制器类型: 同步(Sync):发布操作会阻塞,直到数据被确认发送。异步(Async):发布操作非阻塞,数据发送由后台线程处理。纯同步(PureSync…...
PydanticAI快速入门示例
参考链接:https://ai.pydantic.dev/#why-use-pydanticai 示例代码 from pydantic_ai import Agent from pydantic_ai.models.openai import OpenAIModel from pydantic_ai.providers.openai import OpenAIProvider# 配置使用阿里云通义千问模型 model OpenAIMode…...
Java中HashMap底层原理深度解析:从数据结构到红黑树优化
一、HashMap概述与核心特性 HashMap作为Java集合框架中最常用的数据结构之一,是基于哈希表的Map接口非同步实现。它允许使用null键和null值(但只能有一个null键),并且不保证映射顺序的恒久不变。与Hashtable相比,Hash…...
