【C语言】qsort的秘密

一,本文目标 

qsort函数可以对任意类型数据甚至是结构体内部的数据按照你想要的规则排序,它的功能很强大,可是为什么呢?

我将通过模拟实现qsort函数来让你对这整个过程有一个清晰的深刻的理解。


二,qsort函数原型

void qsort (void* base,//要排序的对象的第一个元素的首地址size_t num, //对象的个数size_t size,//每一个对象的大小 Size in bytesint (*compar)(const void*,const void*));//Pointer to a function that compares two elements.(并且这个函数要自己写)

        qsort函数底层通过快速排序进行,但是这并不是我们感兴趣的点,我想要知道qsort为什么可以对任意类型数据进行排序,与它用什么排序算法排序没有关系,所以,我们用相对简单的冒泡排序来代替快速排序的算法,把冒泡排序赋予可以对任意类型数据进行排序的强大功能。


三,冒泡排序——大数沉底,小数上浮

 对于冒泡排序,其基本思想是大数沉底,小数上浮;

这里直接给出源码:

void Bubble_sort(int arr[], int size)
{int j,i;for (i = 0; i < size-1;i++)//排序趟数等于元素个数-1{int f = 0;for (j = 0; j < size - 1 - i; j++)//每一趟都有一个元素复位,需要排序的次数每次-1{if ( arr[j] > arr[j+1] ){int tem = arr[j];arr[j] = arr[j+1];arr[j+1] = tem;f = 1;}}if (f == 0)	{break;}}

3.1相对于qsort,冒泡排序的局限性

        只能排序整形数据 

四,两个问题:

4.1不定类型比较问题

        1.对于if内部的判断,虽然字符型可以用 >,<,= 比较,但是对于字符串类型,无法通过常规方法比较;

4.2不定类型交换问题

        2.对于交换部分,由于不同元素的类型不同,占用的内存空间也不同,如何判断传入数据的类型,并实现两个数据的交换呢?


五,改造冒泡排序

我们将自己模拟实现的qsort函数称my_qsort,实现如下:


void my_qsort(void* base,size_t sz,size_t width,int (*cmp)(const void* p1,const void* p2))//cmp 是函数指针,在my_sort中被多次调用
{//它指向的函数是int_cmp,int_cmp就是回调函数int i = 0;for(i = 0;i < sz - 1;i++)//趟数不变{int j = 0;for(j = 0;j < sz - i - 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);}}}
}

5.1不同类型比较

我们定义一个cmp函数来实现比较:

5.1.1cmp实参

我们传入排序的数组的首元素地址base,将base强制类型转化为 char* 类型,这样base与整数的运算就只会跨过这个整数个字节;

如果再知道每个元素的大小(长度),那么我们就可以精确的访问到每个元素了;


怎么理解呢? 

 e.g.1:对于int

 图中的base指针的位置是 j == 1的位置

 e.g.2:对于char

 

 图中的base指针的位置是 j == 5 的位置 

5.1.2cmp形参

int int_cmp(const void* p1,const void* p2)
{return *(int*)p1 - *(int*)p2;
}

由于比较的对象是整型,所以定义一个比较整形的函数,void* 类型不能直接解引用,所以将p1,p2强制类型转化,将两数相减返回。

5.2不定类型交换问题

定义交换函数Swap 

void Swap(char* buf1,char* buf2,int width)//按字节逐个交换
{for(int i = 0;i < width;i++){char tem = *buf1;*buf1 = *buf2;*buf2 = tem;buf1++;//如果是整型,则要遍历四个字节buf2++;}
}

这时候,我们就知道定义width的意义了,对于一个数据类型,我们虽然不知道它的类型,但是我们可以根据它的长度,一个一个字节地交换数据。


整体代码运行

对于所有类型,我们的冒泡排序都可以排序了,它的作用与库函数qsort一致,仍然需要我们自己写cmp函数:;

对int型数据的排序:

#include<stdio.h>
#include<string.h>
int int_cmp(const void* p1,const void* p2)
{return *(int*)p1 - *(int*)p2;
}
void Swap(char* buf1,char* buf2,int width)//按字节逐个交换
{for(int i = 0;i < width;i++){char tem = *buf1;*buf1 = *buf2;*buf2 = tem;buf1++;//如果是整型,则要遍历四个字节buf2++;}
}void my_qsort(void* base,size_t sz,size_t width,int (*cmp)(const void* p1,const void* p2))//cmp 是函数指针,在my_sort中被多次调用
{//它指向的函数是int_cmp,int_cmp就是回调函数int i = 0;for(i = 0;i < sz - 1;i++)//趟数不变{int j = 0;for(j = 0;j < sz - i - 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 test1(void)
{int arr[] = {2,5,8,9,6,3,1,4,7,0};int sz = sizeof(arr)/sizeof(arr[0]);my_qsort(arr,sz,sizeof(arr[0]),int_cmp);for(int i = 0;i < sz;i++){printf("%d ",arr[i]);}
}int main()
{test1();return 0;
}

对于结构体类型,也可根据结构体内部某一元素的特征排序:

结构体类型

对结构体内的int型数据排序:


struct stu
{char name[20];int age;
};int struct_cmp_by_age(const void* p1,const void* p2)
{return ((struct stu*)p1)->age - ((struct stu*)p2)->age;
}void test2(void)
{struct stu arr[] = {{"zhangsan",18},{"lisi",25},{"wangwu",30},{"xiaoming",40}};int sz = sizeof(arr)/sizeof(arr[0]);my_qsort(arr,sz,sizeof(arr[0]),struct_cmp_by_age);for(int i = 0;i < sz;i++){printf("%s %d\n",arr[i].name,arr[i].age);}
}int main()
{test2();return 0;
}

对结构体内的char型数据排序:


struct stu
{char name[20];int age;
};int struct_cmp_by_name(const void* p1,const void* p2)
{return strcmp(((struct stu*)p1)->name,((struct stu*)p2)->name);
}void test3(void)
{struct stu arr[] = {{"zhangsan",18},{"lisi",25},{"wangwu",30},{"xiaoming",40}};int sz = sizeof(arr)/sizeof(arr[0]);my_qsort(arr,sz,sizeof(arr[0]),struct_cmp_by_name);for(int i = 0;i < sz;i++){printf("%s %d\n",arr[i].name,arr[i].age);}
}int main()
{//test1();//test2();test3();return 0;
}

1.对字符串的比较用到<string.h>中的strcmp函数,而它的返回值正好符合qsort函数第四个参数:函数的要求 

 

 

第一个字符串大于第二个,strcmp返回大于0的数,对应qsort函数要求第四个函数的返回值大于0,表示第一个参数大一第二个。 


本文回顾 

目录

一,本文目标 

二,qsort函数原型

三,冒泡排序——大数沉底,小数上浮

3.1相对于qsort,冒泡排序的局限性

四,两个问题:

4.1不定类型比较问题

4.2不定类型交换问题

五,改造冒泡排序

5.1不同类型比较

5.1.1cmp实参

5.1.2cmp形参

5.2不定类型交换问题

整体代码运行

对int型数据的排序:

结构体类型

对结构体内的int型数据排序:

对结构体内的char型数据排序:


完~

未经作者同意禁止转载

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

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

相关文章

leetcode刷题详解一

算法题常用API std::accumulate 函数原型&#xff1a; template< class InputIt, class T > T accumulate( InputIt first, InputIt last, T init );一般求和的&#xff0c;代码如下&#xff1a; int sum accumulate(vec.begin() , vec.end() , 0);详细用法参考 lo…

【python海洋专题四十七】风速的风羽图

【python海洋专题四十七】风速的风羽图 图片 往期推荐 图片 【python海洋专题一】查看数据nc文件的属性并输出属性到txt文件 【python海洋专题二】读取水深nc文件并水深地形图 【python海洋专题三】图像修饰之画布和坐标轴 【Python海洋专题四】之水深地图图像修饰 【Pyth…

记一次linux操作系统实验

前言 最近完成了一个需要修改和编译linux内核源码的操作系统实验&#xff0c;个人感觉这个实验还是比较有意思的。这次实验总共耗时4天&#xff0c;从对linux实现零基础&#xff0c;通过查阅资料和不断尝试&#xff0c;直到完成实验目标&#xff0c;在这过程中确实也收获颇丰&…

pytorch模型优化简介,未完结版

如有帮助&#xff0c;点赞收藏关注&#xff01; 如需转载&#xff0c;请注明出处&#xff01; 今天来介绍torch模型中的优化器 优化是指在每个训练步骤中调整模型参数以减少模型误差的过程。 优化算法定义如何执行这个过程 所有优化逻辑都封装在优化器对象中。在这里&#xf…

【黑马甄选离线数仓day04_维度域开发】

1. 维度主题表数据导出 1.1 PostgreSQL介绍 PostgreSQL 是一个功能强大的开源对象关系数据库系统&#xff0c;它使用和扩展了 SQL 语言&#xff0c;并结合了许多安全存储和扩展最复杂数据工作负载的功能。 官方网址&#xff1a;PostgreSQL: The worlds most advanced open s…

音视频项目——RTSP服务器解析(1)

介绍 利用线程池&#xff0c;实现 RTSP 服务器的高并发请求处理。 RTSP 是音视频的控制视频的协议&#xff0c;如果您还不了解&#xff0c;可以看看之前我解析 RTSP 协议的文章。音视频协议解析(RTP/RTCP/RTSP/RTMP)——RTSP解析-CSDN博客 解析 我们先来看 RTP 的实现。RTP 是音…

Django框架之auth模块

目录 一、Auth模块引入 二、创建超级用户(管理员) 三、依赖于auth_user表完成登录注册功能 【1】基础登陆 【2】保存用户状态 【3】登录后跳转 (1) 登录后才能访问页面 -- 局部配置 (2) 登录后才能访问页面 -- 全局配置 (3) 小结 三、修改密码 四、注销 五、注册…

Springboot将多个图片导出成zip压缩包

Springboot将多个图片导出成zip压缩包 将多个图片导出成zip压缩包 /*** 判断时间差是否超过6小时* param startTime 开始时间* param endTime 结束时间* return*/public static boolean isWithin6Hours(String startTime, String endTime) {// 定义日期时间格式DateTimeFormatt…

【数据结构】—搜索二叉树(C++实现,超详细!)

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;消えてしまいそうです—真夜中 1:15━━━━━━️&#x1f49f;──────── 4:18 &#x1f504; ◀️ ⏸ ▶️…

函数计算的新征程:使用 Laf 构建 AI 知识库

Laf 已成功上架 Sealos 模板市场&#xff0c;可通过 Laf 应用模板来一键部署&#xff01; 这意味着 Laf 在私有化部署上的扩展性得到了极大的提升。 Sealos 作为一个功能强大的云操作系统&#xff0c;能够秒级创建多种高可用数据库&#xff0c;如 MySQL、PostgreSQL、MongoDB …

js实现获取原生form表单的数据序列化表单以及将数组转化为一个对象obj,将数组中的内容作为对象的key转化为对象,对应的值转换为对象对应的值

1.需求场景 哈喽 大家好啊&#xff0c;今天遇到一个场景&#xff0c; js实现获取原生form表单的数据序列化表单以及将数组转化为一个对象obj&#xff0c;将数组中的内容作为对象的key转化为对象&#xff0c;对应的值转换为对象对应的值 数组对象中某个属性的值&#xff0c;转…

元宇宙现已开放!

在 2023 年 11 月 3 日 The Sandbox 首个全球创作者日上&#xff0c;The Sandbox 联合创始人 Arthur Madrid 和 Sebastien Borget 宣布元宇宙已开放&#xff0c;已创作完整体验的 LAND 持有者可以自行将体验发布至 The Sandbox 地图上。 精选速览 LAND 持有者&#xff1a;如果…

在JVM中 判定哪些对象是垃圾?

目录 垃圾的条件 1、引用计数法 2、可达性分析 3、强引用 4、软引用 5、弱引用 6、虚引用 判断垃圾的条件 在Java虚拟机&#xff08;JVM&#xff09;中&#xff0c;垃圾收集器负责管理内存&#xff0c;其中的垃圾收集算法用于确定哪些对象是垃圾&#xff0c;可以被回收…

VBA即用型代码手册之工作薄的关闭保存及创建

我给VBA下的定义&#xff1a;VBA是个人小型自动化处理的有效工具。可以大大提高自己的劳动效率&#xff0c;而且可以提高数据的准确性。我这里专注VBA,将我多年的经验汇集在VBA系列九套教程中。 作为我的学员要利用我的积木编程思想&#xff0c;积木编程最重要的是积木如何搭建…

[Latex] Riemann 问题中的激波,接触间断,膨胀波的 Tikz 绘图

Latex 代码 \begin{figure}\begin{subfigure}[b]{0.32\textwidth}\centering\resizebox{\linewidth}{!}{\begin{tikzpicture}\coordinate (o) at (0,0);\coordinate (Si) at (2.5,2.5);\coordinate (x) at (1,0);\draw[->] (0,0) -- (3,0) node[right] {$x$};\draw[->] …

ArkTS-自定义组件学习

文章目录 创建自定义组件页面和自定义组件生命周期自定义组件和页面的区别页面生命周期(即被Entry修饰的组件)组件生命周期(即被Component修饰的组件) Builder装饰器&#xff1a;自定义构建函数按引用传递参数按值传递参数 BuilderParam装饰器&#xff1a;引用Builder函数 这个…

Python 将列表拼接为一个字符串,Python join

目录 join方法的源码&#xff1a; 列表数据为字符串 列表数据为数字 三引号也可以使用join join方法的源码&#xff1a; def join(self, abNone, pqNone, rsNone): # real signature unknown; restored from __doc__"""Concatenate any number of strings.T…

harmonyos应用开发者高级认证考试部分答案

1只要使用端云一体化的云端资源就需要支付费用&#xff08;错&#xff09; 2所有使用Component修饰的自定义组件都支持onPageShow&#xff0c;onBackPress和onPageHide生命周期函数。&#xff08;错&#xff09; 3 HarmonyOS应用可以兼容OpenHarmony生态&#xff08;对&#…

一文读懂如何安全地存储密码

目录 引言 明文存储 基本哈希存储 加盐哈希存储 适应性哈希算法 密码加密存储 小结 引言 密码是最常用的身份验证手段&#xff0c;既简单又高效。密码安全是网络安全的基石&#xff0c;对保护个人和组织信息的安全具有根本性的作用。然而有关密码泄漏的安全问题一再发生…

生物动力葡萄酒和有机葡萄酒一样吗?

农业维持了数十万年的文明&#xff0c;但当人类以错误的方式过多干预&#xff0c;过于专注于制造和操纵产品时&#xff0c;农业往往会失败。如果我们的目标是获得最高质量的收成&#xff0c;并长期坚持我们的做法&#xff0c;我们就必须与土地打交道。 当我们开始寻找生物动力…