C语言:深入浅出qsort方法,编写自己的qsort完成冒泡排序

目录

什么是qsort? 

函数原型

比较函数 compar

排序整型数组

排序结构体数组

根据成员字符排序

 strcmp函数

根据成员整型排序

自定义qsort实现冒泡排序 

qsort的实现原理 

具体步骤

快速排序示例代码: 


什么是qsort? 

qsort是 C 语言标准库<stdib.h>中的一个函数,用于对数组进行快速排序,如下 

  • base:指向需排序的数组指针(同数组首元素地址)
  • num:数组中的元素个数。
  • size:每个元素的大小(以字节为单位)。
  • compar:比较函数指针,用于定义排序顺序。

函数原型

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

比较函数 compar

比较函数 compar 负责定义数组元素的排序顺序。它应该接受两个参数,每个参数的类型都是 const void *,并返回一个整型值。比较函数的返回值定义了元素之间的排序关系:

  • 如果返回值小于 0,则第一个参数应该排在第二个参数之前。
  • 如果返回值等于 0,则两个参数的相对位置不变。
  • 如果返回值大于 0,则第一个参数应该排在第二个参数之后。

排序整型数组

void是无具体类型的指针,可以接受任意类型的地址,但是不能解引用,也不能+-操作,因为它不知道自己有几个字节的权限

int a = 10;
void *pv = &a;

compare函数形参中:因为指针类型是void,不能解引用,需要强制类型转换为int再解引用(根据自己的数据类型来决定是int还是char还是结构体)

#include <stdio.h>
#include <stdlib.h>int compare(const void* a, const void* b) {return (*(int*)a - *(int*)b);
}int main() {int arr[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};int size = sizeof(arr) / sizeof(arr[0]);qsort(arr, size, sizeof(int), compare);for (int i = 0; i < size; i++) {printf("%d ", arr[i]); // 1 1 2 3 3 4 5 5 5 6 9}return 0;
}

排序结构体数组

根据成员字符排序

struct Stu
{char name[20];int age;
};int cmp_struct_name(const void *e1, const void *e2)
{return strcmp(((struct Stu *)e1)->name, ((struct Stu *)e2)->name); // lisi、wangwu、zhangsan
}struct Stu s[] ={{"zhangsan", 22},{"lisi", 24},{"wangwu", 23}};
int struct_size = sizeof(s) / sizeof(s[0]);
qsort(s, struct_size, sizeof(s[0]), cmp_struct_name);

 strcmp函数

  • strcmp函数用于比较字符串大小 ——> ==0 >0 <0
  • strcmp() 函数比较字符串时会逐个字符地比较它们的 ASCII 码值,直到有字符不相同时才停止比较。
  • 如果其中一个字符串已经结束了,而另一个字符串还没有结束,那么这个字符串就被认为比另一个字符串更小。
  • 头文件<string.h>

根据成员整型排序

struct Stu
{char name[20];int age;
};int cmp_struct_age(const void *e1, const void *e2)
{/* 从小到大 */return ((struct Stu *)e1)->age - ((struct Stu *)e2)->age; // 22 23 24
}struct Stu s[] ={{"zhangsan", 22},{"lisi", 24},{"wangwu", 23}};
int struct_size = sizeof(s) / sizeof(s[0]);
qsort(s, struct_size, sizeof(s[0]), cmp_struct_age);

自定义qsort实现冒泡排序 

/* 最终交换字节 */
void Swap(char *buf1, char *buf2, int width)
{for (int i = 0; i < width; i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void my_qsort(void *base, int sz, int width, int (*cmp)(const void *e1, const void *e2))
{for (int i = 0; i < sz - 1; i++){for (int j = 0; j < sz - 1 - i; j++){/**实参传进去的是待比较的元素的地址之所以用char类型指针转换,是因为最保险,什么类型指针能访问的字节大小都可以从最小字节1开始活动比如这里,数据是int类型,那么cmp的实参第一个是 0*4,第二是 1*4,即第一个int数的地址,第二个int数的地址*/if (cmp((char *)base + j * width, (char *)base + (j + 1) * width) > 0){Swap((char *)base + j * width, (char *)base + (j + 1) * width, width);}}}
}my_qsort(arr, sz, sizeof(arr[0]), cmp_int);
for (int i = 0; i < sz; i++)
{printf("%d ", arr[i]); // 9 8 7 6 5 4 3 2 1 0
}

qsort的实现原理 

qsort方法,它使用了一种叫做快速排序(Quicksort)的算法。快速排序是一种分治的排序算法,其基本思想是选择一个基准值,然后将数组中小于基准值的元素放到基准值的左边,大于基准值的元素放到基准值的右边,最终使得基准值左边的所有元素都小于等于基准值,右边的所有元素都大于等于基准值。然后递归地对基准值两边的子数组进行排序,直到整个数组有序为止。

具体步骤

  1. 选择基准值:从数组中选择一个元素作为基准值(通常选择第一个或者中间的元素)。
  2. 分区过程:将数组中的元素重新排列,将小于或等于基准值的放在基准值的左边,大于基准值的放在右边。
  3. 递归排序:对基准值左右两个子数组分别递归执行上述步骤。

快速排序示例代码: 

void quicksort(int arr[], int low, int high) {if (low < high) {int pivot = partition(arr, low, high);quicksort(arr, low, pivot - 1);quicksort(arr, pivot + 1, high);}
}int partition(int arr[], int low, int high) {int pivot = arr[low];int i = low, j = high;while (i < j) {while (i < j && arr[j] >= pivot) {j--;}arr[i] = arr[j];while (i < j && arr[i] <= pivot) {i++;}arr[j] = arr[i];}arr[i] = pivot;return i;
}

在这个示例中,quicksort 函数进行了递归排序,partition 函数则负责进行分区操作。具体而言,partition 函数以第一个元素作为基准值,然后对数组进行分区操作,并返回新的基准值的位置。对两个子数组进行的递归排序最终能够完成整个数组的排序过程。

快速排序的时间复杂度为 O(n log n),这使得快速排序成为一种非常高效的排序算法。在最好情况下,快速排序的时间复杂度是 O(n log n),而在最坏情况下,时间复杂度为 O(n^2),但平均情况下时间复杂度依然是 O(n log n)。当然,这也取决于选择的基准值和数组的初始状态。

同时,快速排序是一种原地排序算法,它不需要额外的存储空间来存储临时数据,只需要对原始数组进行原地交换和重排列。

总结来说,快速排序通过选择基准值、分区和递归排序等步骤,能够高效地对数组进行排序,在平均情况下的时间复杂度为 O(n log n),适用于大多数场景下的排序需求。因此,qsort 函数使用的快速排序算法在实际应用中具有较高的效率和性能表现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/185437.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[AndroidStudio]_[初级]_[修改虚拟设备镜像文件的存放位置]

场景 在使用Android Studio的虚拟设备运行App时&#xff0c;需要创建很大镜像文件。这些镜像文件一般都在系统盘&#xff0c;导致系统盘占用增大。怎么把这些镜像的存放路径设置在其他盘&#xff1f; 说明 虚拟设备的和它的镜像默认是放在用户目录\.android\avd位置。如果是在…

深入OpenCV Android应用开发

前言 OpenCV是Open Source Computer Vision library(开源的计算机视觉库)的缩写。它是使用最广泛的计算机视觉库。Opencv是计算机视觉领域常用的操作函数的集合&#xff0c;其自身由C/C编写而成&#xff0c;同时也提供了对Python、Java以及任意JVM语言的封装。考虑到大部分And…

统信UOS_麒麟KYLINOS创建网页桌面快捷方式

原文链接&#xff1a;统信UOS/麒麟KYLINOS创建网页桌面快捷方式 hello&#xff0c;大家好啊&#xff0c;今天给大家带来一篇使用命令行在统信UOS/麒麟KYLINOS创建网页桌面快捷方式的文章&#xff0c;主要用于构建云桌面模板及镜像模板的时候使用&#xff0c;欢迎大家浏览分享转…

数据库数据恢复—无备份,未开启binlog的MySQL误删除怎么恢复数据

数据库数据恢复环境&#xff1a; 一台本地windows sever操作系统服务器&#xff0c;服务器上部署mysql数据库单实例&#xff0c;引擎类型为innodb&#xff0c;表内数据存储所使用表空间类型为独立表空间。无数据库备份&#xff0c;未开启binlog。 数据库故障&分析&#xf…

05预测识别-依托YOLO V8进行训练模型的识别——对视频中的目标进行跟踪统计

上文中详细介绍了如何对视频进行抽帧,并对帧的图像进行目标识别。但在日常工作中,我们也会遇到需要对目标进行跟踪统计的情况,比如我们需要连续统计某一类目标有多少个的时候,如果单纯从帧中抽取图像的话,系统将无法判断是否为同一目标,从而造成目标数量统计的重复,导致…

SpringBoot整合Swagger3,赶紧整起来!

文章目录 一、Swagger是什么&#xff1f;二、使用步骤1.引入swagger3依赖2.添加swagger.conf配置类3.添加application.yml配置4.查看是否整合成功5.常用注解6.swagger美化 总结 一、Swagger是什么&#xff1f; Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用…

12 tcp协议详解

1、tcp的本性 tcp是一个悲观者&#xff0c;生下来就不信任网络&#xff0c;任务会发生丢包等&#xff0c;所以要从算法层面来保证可靠性。 2、TCP 包头格式 tcp的包头格式比UDP要复杂的多。 1.源端口号和目标端口号是不可少的&#xff0c;这一点和 UDP 是一样的。如果没有…

图形界面应用案例——关灯游戏(以及扩展)(python)

7.8 图形界面应用案例——关灯游戏 题目: [案例]游戏初步——关灯游戏。 关灯游戏是很有意思的益智游戏,玩家通过单击关掉(或打开)一盏灯。如果关(掉(或打开)一个电灯,其周围(上下左右)的电灯也会触及开关,成功地关掉所有电灯即可过关。 图7-43 关灯游戏运行效…

安防监控系统视频融合平台EasyCVR页面地图功能细节详解

安防监控视频汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&#xff…

62、使用python进行rk3588开发板进行推流亚马逊云服务上,进行实时播放

基本思想:之前写了一套c++的推理和视频编解码,使用rk3588的mpp硬件进行编码和解码,然后使用RTSPServer进行推流,总是有问题,虽然可以使用ffplay和vlc进行拉取和播放,但是就是无法使用gstreamer推流到亚马逊云服务上,因为项目需求的紧急,所以先用python把流程跑同,后续…

linux基础指令【上篇】

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 引用 01. ls 指令2. pwd命…

FPGA的元素组件

注意&#xff1a;关于FPGA的元素这一块儿内容&#xff0c;稍有出入。例如&#xff1a;吉姆莱丁 著&#xff0c;陈会翔 译&#xff0c;由清华大学出版社出版的《构建高性能嵌入式系统》中提到&#xff1a;FPGA通常由查找表、触发器、块RAM、DSP切片、及其他功能元件等元素组成。…

互联网金融风控常见知识点

1.怎么做互联网金融风控 首先风险不是都是坏的&#xff0c;风险是有价值的。也就是风险的VaR值(Value at Risk) 对于互联网信贷风控&#xff0c;是要把风险和收益做到更合理的平衡&#xff0c;在控制风险水平的情况下使得收益更高。 所以&#xff0c;做风控的不是一味地追求耕…

设计模式—结构型模式之桥接模式

设计模式—结构型模式之桥接模式 将抽象与实现解耦&#xff0c;使两者都可以独立变化。 在现实生活中&#xff0c;某些类具有两个或多个维度的变化&#xff0c;如图形既可按形状分&#xff0c;又可按颜色分。如何设计类似于 Photoshop 这样的软件&#xff0c;能画不同形状和不…

flink的带状态的RichFlatMapFunction函数使用

背景 使用RichFlatMapFunction可以带状态来决定如何对数据流进行转换&#xff0c;而且这种用法非常常见&#xff0c;根据之前遇到过的某个key的状态来决定再次遇到同样的key时要如何进行数据转换&#xff0c;本文就来简单举个例子说明下RichFlatMapFunction的使用方法 RichFl…

OceanBase 如何通过日志观测冻结转储流程?

本文旨在通过日志解析 OceanBase 的冻结转储流程&#xff0c;以其冻结检查线程为切入点&#xff0c;以租户&#xff08;1002&#xff09;的线程名为例。 作者&#xff1a;陈慧明&#xff0c;爱可生测试工程师&#xff0c;主要参与 DMP 和 DBLE 自动化测试项目。 爱可生开源社区…

技术分享|基于 Cluster API 的 Kubernetes 集群生命周期管理

作者&#xff1a;SmartX SKS 产品研发工程师 杨海剑 背景 容器的发展催生了容器编排技术&#xff0c;而容器编排技术反过来又推动了容器的发展。容器编排领域则一度出现了 Swarm、Mesos 和 Kubernetes 等百家争鸣的局面。但随着 Kubernetes 脱颖而出&#xff0c;Kubernetes 成为…

早安心语微语早读,好好善待自己,珍惜今天,期待明天

1、保持阳光心态&#xff0c;积极面对人生&#xff0c;每个人&#xff0c;都沿着不同的轨道在活着&#xff0c;人生是一趟单程车&#xff0c;我们最应该做的&#xff0c;就是好好善待自己&#xff0c;珍惜今天&#xff0c;期待明天&#xff0c;那些走过的&#xff0c;错过的&am…

零代码编程:用ChatGPT批量将Mp4视频转为Mp3音频

文件夹中有很多mp4视频文件&#xff0c;如何利用ChatGPT来全部转换为mp3音频呢&#xff1f; 在ChatGPT中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个批量将Mp4视频转为Mp3音频的任务&#xff0c;具体步骤如下&#xff1a; 打开文件夹&#xff1a;…

2023最新版JavaSE教程——第4天:流程控制语句之循环语句

目录 一、循环语句二、for循环2.1 基本语法2.2 应用举例2.3 练习 三、while循环3.1 基本语法3.2 应用举例3.3 练习 四、do-while循环4.1 基本语法4.2 应用举例4.3 练习4.4 对比三种循环结构4.5 "无限"循环4.5.1 基本语法4.5.2 应用举例 4.6 嵌套循环(或多重循环)4.6.…