深入解析 qsort 函数(下),用冒泡排序模拟实现 qsort 函数
前言:对于库函数有适当了解的朋友们,对于 qsort 函数想必是有认知的,因为他可以对任意数据类型进行排序的功能属实是有点厉害的,本次分享,笔者就给大家带来 qsort 函数的全面的解读
本次知识的分享笔者分为上下俩卷文章进行讲解,在本篇文章中,给大家带来 qsort 函数的函数内部构造解析,以及如何通过冒泡排序模拟实现 qsort 的函数功能
上卷:深入解析 qsort 排序(上),它为什么是万能排序?
目录
一.模拟实现函数参数
二.确定算法大体的框架
三.实现函数的判断逻辑、
函数指针部分:
具体判断方法函数:
四.交换函数的实现
代码实现:
五.完整功能测试
六.完整代码
一.模拟实现函数参数
首先我们打开 cplusplus 官网查看 qsort 的参数要求和含义
qsort - C++ Reference (cplusplus.com)
具体参数信息如下
- void* base:待排序数组的第一个元素的地址
- size_t num:待排序数组的元素个数
- size_t size:待排序数组中一个元素的大小
- int (*compar)(const void*, const void*):函数指针-cmp指向了一个函数,这个函数是用来比较两个元素的,e1和e2中存放的是需要比较的两个元素的地址
根据上面的定义说明,我们对函数参数进行模拟
void my_qsort(void* base, size_t num, size_t size, int (*compare)(const void* e1, const void* e2))
二.确定算法大体的框架
正如标题所说,笔者这里使用较为容易理解的冒泡排序,当然其他的快速排序,选择排序,堆排序,希尔排序等算法都是可以使用的,笔者这里只是为了方便讲解具体实现流程和细节,所以选择的冒泡排序的算法
在选择好了使用什么算法进行模拟实现后,我们就需要来设计大体的框架了,首先,我们列出一般冒泡排序的形式,然后在这基础上进行删改
for (int i = 0; i < num - 1; i++){//每一趟for (int j = 0; j < num - i - 1; j++){//排序:前一个元素大于后一个就进行交换if (arr[j] > arr[j + 1]){int temp = 0;temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}
在这里,我们需要修改的地方主要是俩点
- 判断部分,考虑到不同数据的复杂类型,判断部分不能再使用这样简单数组的方式进行判断,应该设计成一个函数指针,针对不同的数据类型调用不同的函数来进行不同的判断
- 交换部分 ,考虑不同的数据的复杂类型,交换部分也不能使用 int 型的临时变量来进行交换数据,应该设计成对任意数据类型都能交换的方式,也就是对字节直接进行操作,对于不同的数据类型,我们直接对其内存存储的字节进行交换,这样就能达到目的和要求
三.实现函数的判断逻辑、
函数指针部分:
首先,为了实现不同的判断方法,我们要有一个函数指针去调用不同的判断方法,我们根据 qsort 的参数要求设计如下函数指针
int (*compare)(const void* e1, const void* e2)
这个函数指针有俩个参数,分别代表着需要判断排序的数据,我们调用的参数的返回值是有要求的,如下图
那我们就可以将这个判断逻辑的函数的返回值来作为 if 语句判断依据,实现实例如下
if (compare((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
{}
这代表着,如果判断函数返回值大于0,那我们就执行交换操作,如果等于0或者小于0就不执行交换操作,那我们实现了函数调用的接口,我们还得具体的实现判断逻辑函数,方便进行调用
具体判断方法函数:
根据我们在上卷文章中讲解的判断逻辑和实现方法,我们可以同样试用在这里,具体细节如有不懂可以访问下面的链接
深入解析 qsort 排序(上),它为什么是万能排序?
//比较函数--整形
int comp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}//比较函数--浮点型
int comp_float(const void* a, const void* b)
{return *(float*)b - *(float*)a;
}//比较函数--按照年龄
int comp_age(const void* a, const void* b)
{return ((struct stu*)b)->age - ((struct stu*)a)->age;
}//比较函数--按照姓名
int comp_name(const void* a, const void* b)
{return strcmp(((struct stu*)a)->name, ((struct stu*)b)->name);
}
四.交换函数的实现
首先,我们要确定我们要进行交换的对象,对于冒泡排序来说,需要交换的只是前后俩个元素,那我们拿到他们的地址就可以进行交换了,我们具体实现如下
//交换
swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
这里分别对三个参数进行一下解释:
- (char*)base + j * size :确定要交换的第一个数据的地址
- (char*)base + (j + 1) * size :确定要交换的第二个数据的地址
- size :确定这俩个数据直接隔了多少个字节,方便逐字节进行操作
对于参数为什么这样设定,有小伙伴可能不理解,下面笔者就以 int 类型举例,我们知道,int 类型占 4 个字节,而 char* 指针解引用可以访问一个字节,而数据在内存中存储的最小字节都是 1 个字节,那我们就可以 通过 char* 指针来逐个访问字节,就像下面这张图一样,我们逐个交换俩个数据中的每一个字节,在交换完成后,我们就相当于把这俩个数字进行了交换
在逐个交换字节后,宏观上给我们的感受就是直接交换了我们的数据内容,也就是说我们使用俩个 char* 指针分别访问要交换的俩个在数据,每一次交换一个字节后,指针加一,我们就可以继续访问交换后面的字节,在这里循环往复交换 size 个字节后,这俩个数据就被我们完全交换了
代码实现:
注意,在具体写代码的过程中不要非法访问空间,在下方代码的注释部分,笔者对错误代码进行了注释,大家在写过程中一定得注意,在指针访问一些只读区域或者不是我们申请的空间的区域就会出现段错误,就像注释部分的代码,我们定义一个空指针,然后再对这个空指针进行解引用访问赋值的操作就会出现报错
//交换函数
void swap(char* buf1, char* buf2, size_t size)
{//逐个字节进行交换,一共有size个字节for (int i = 0; i < size; i++){//错误示范//char* temp = 0;//*temp = *buf1;//*buf1 = *buf2;//*buf2 = *temp;//buf1++;//buf2++;//正确示范char temp = *buf1;*buf1 = *buf2;*buf2 = temp;buf1++;buf2++;}
}
五.完整功能测试
我们仍然使用在上篇文章中的用例进行测试,观察我们设计的函数能不能完成我们预期的功能,在这里对整形数组,浮点型数组,结构体数组进行测试
int main()
{int arr1[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr1) / sizeof(arr1[0]);print1(arr1);my_qsort(arr1, sz, sizeof(int), comp_int);print1(arr1);float arr2[5] = { 1.2,3.4,5.6,7.8,9.9 };print2(arr2);my_qsort(arr2, 5, sizeof(float), comp_float);print2(arr2);struct stu arr3[] = { {"zhangsan",19},{"lisi",20},{"wangswu",21} };my_qsort(arr3, 3, sizeof(arr3[0]), comp_name);my_qsort(arr3, 3, sizeof(arr3[0]), comp_age);system("pause");return 0;
}
观察结果:
我们可以发现和我们预期的一样,对于不同数据类型,我们自己模拟的函数都可以实现排序功能
六.完整代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<windows.h>//结构体
struct stu
{char name[20];int age;
};void print1(int* arr)
{for (int i = 0; i < 10; i++){printf("%d ", *(arr + i));}printf("\n");
}void print2(float* arr)
{for (int i = 0; i < 5; i++){printf("%.2f ", *(arr + i));}printf("\n");
}//比较函数--整形
int comp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}//比较函数--浮点型
int comp_float(const void* a, const void* b)
{return *(float*)b - *(float*)a;
}//比较函数--按照年龄
int comp_age(const void* a, const void* b)
{return ((struct stu*)b)->age - ((struct stu*)a)->age;
}//比较函数--按照姓名
int comp_name(const void* a, const void* b)
{return strcmp(((struct stu*)a)->name, ((struct stu*)b)->name);
}//交换函数
void swap(char* buf1, char* buf2, size_t size)
{//逐个字节进行交换,一共有size个字节for (int i = 0; i < size; i++){//错误示范//char* temp = 0;//*temp = *buf1;//*buf1 = *buf2;//*buf2 = *temp;//buf1++;//buf2++;//正确示范char temp = *buf1;*buf1 = *buf2;*buf2 = temp;buf1++;buf2++;}
}//void qsort(void* base, size_t num, size_t size,int (*compar)(const void*, const void*));
void my_qsort(void* base, size_t num, size_t size, int (*compare)(const void* e1, const void* e2))
{//使用冒泡排序for (int i = 0; i < num - 1; i++){//每一趟for (int j = 0; j < num - i - 1; j++){//排序:前一个元素大于后一个if (compare((char*)base + j * size, (char*)base + (j + 1) * size) > 0){//交换swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}
}int main()
{int arr1[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr1) / sizeof(arr1[0]);print1(arr1);my_qsort(arr1, sz, sizeof(int), comp_int);print1(arr1);float arr2[5] = { 1.2,3.4,5.6,7.8,9.9 };print2(arr2);my_qsort(arr2, 5, sizeof(float), comp_float);print2(arr2);struct stu arr3[] = { {"zhangsan",19},{"lisi",20},{"wangswu",21} };my_qsort(arr3, 3, sizeof(arr3[0]), comp_name);my_qsort(arr3, 3, sizeof(arr3[0]), comp_age);system("pause");return 0;
}
本次分享的俩篇文章就到此为止啦,希望我的分享对您有所帮助,文章中如有错误,欢迎积极指正,您的认可就是我最大的动力,那我们下次分享再见
相关文章:
深入解析 qsort 函数(下),用冒泡排序模拟实现 qsort 函数
前言:对于库函数有适当了解的朋友们,对于 qsort 函数想必是有认知的,因为他可以对任意数据类型进行排序的功能属实是有点厉害的,本次分享,笔者就给大家带来 qsort 函数的全面的解读 本次知识的分享笔者分为上下俩卷文章…...
Azure + React + ASP.NET Core 项目笔记一:项目环境搭建(二)
有意义的标题 pnpm 安装umi4 脚手架搭建打包语句变更Visual Studio调试Azure 设置变更发布 pnpm 安装 参考官网,或者直接使用npm安装 npm install -g pnpmumi4 脚手架搭建 我这里用的umi4,官网已附上 这里需要把clientapp清空,之后 cd Cl…...
Vmware通过VMware tools设置共享文件夹
步骤说明: 先安装VMware tools,再设置共享文件夹即可。 写在前面: 刚安装虚拟机时,窗口可能显得太小,这是窗口分辨率没有调整导致的。 点击设置->显示->分辨率调整即可 一、安装VMware tools 1.1 点击虚拟机…...
RPA机器人流程自动化专题培训大纲 (针对大学生的版本)
一、课程简介 RPA机器人流程自动化是一种新兴的技术,它通过软件机器人模拟人类操作计算机完成重复性任务,从而实现业务流程的自动化。本课程旨在介绍RPA机器人流程自动化的基本概念、原理和应用,并通过实践案例演示如何应用RPA机器人流程自动…...
数据在内存中的存储——练习4
题目: int main() {char a[1000];int i;for(i0; i<1000; i){a[i] -1-i;}printf("%d",strlen(a));return 0; }思路分析: 已知条件: 通过循环遍历,我们得到的结果是 -1、-2、-3、-4等等。这些是数组内部的存储的元…...
Python 06 之面向对象基础
😀前言 在日常编程和软件开发中,我们通常会遇到各种各样的问题,其中很多问题都可以通过面向对象的程序设计方法来解决。面向对象编程不仅可以使代码更加组织化和系统化,而且还可以提高代码的重用性和可维护性。 . 在本教程中&…...
去除pdf/word的水印艺术字
对于pdf中的水印如果无法去除水印,则先另存为word,然后再按下面办法处理即可: 查看宏,创建:删除艺术字 添加内容: Sub 删除艺术字()Dim sh As ShapeFor Each sh In ActiveDocument.ShapesIf sh.Type msoT…...
【Linux】使用 Alist 实现阿里云盘4K播放
一、安装 Alist 官方文档 默认安装在 /opt/alist 中 curl -fsSL "https://alist.nn.ci/v3.sh" | bash -s install自定义安装路径,将安装路径作为第二个参数添加,必须是绝对路径,如果路径以 alist 结尾,则直接安装到给定…...
Gof23设计模式之状态模式
1.概述 【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能…...
如何免费下载RunWayML产生的视频文件
问题: 首先没有下载的按钮。 其次如果直接“视频另存为”菜单,报错。 解决方案: 1)复制视频链接。 2)新开chrome,在url中粘贴上一步的url路径。 3)当看到视频后,在视频上面右键“…...
9.14 C++作业
仿照vector手动实现自己的myVector,最主要实现二倍扩容功能 #include <iostream>using namespace std;template <typename T> class Myvector {T *data; //存储数据的数组int len; //当前数组的长度int mycapa; //容纳数据的总容量public://…...
java关于文件记录篇章之文件夹创建篇
今天,创建一个文件夹目录的时候,创建多级目录的时候发现,自己老是创建失败,但是系统显示文件夹创建成功,但是你去找文件夹的时候,又发现创建失败,这里在我成功之后封装了一个创建文件夹的创建对…...
显示器显示的画面突然偏红色如何解决
显示器显示的画面突然偏红色如何解决 1. 概述2. 解决方法结束语 1. 概述 显示器显示的画面突然偏红色 ,使用向日葵远程电脑,看到的画面是正常的,但是显示器上的画面确还是骗红的,这时候就需要看一下是不是开启了系统也夜间模式&a…...
【element-ui】 el-table 表格动态合并相同数据单元格最全教程,可指定列+自定义合并条件,附完整代码
el-table合并单元格 1.固定合并 官方挺提供的合并具体某行列的方法:el-table合并行或列通过给table传入span-method方法可以实现合并行或列,方法的参数是一个对象,里面包含当前行row、当前列column、当前行号rowIndex、当前列号columnIndex四个属性。 该函数可以返回一个包含…...
管理方法论:6. 正视团队冲突——化解危机,长治久安
概念 团队冲突指的是两个或两个以上的团队在目标、利益、认识等方面互不相容或互相排斥,从而产生心理或行为上的矛盾,导致抵触、争执或攻击事件。 参考: https://baike.baidu.com/item/%E5%9B%A2%E9%98%9F%E5%86%B2%E7%AA%81/6747073 htt…...
基于SpringBoot的一套强大后台管理系统
概述 一个功能强大而完善的后台管理系统框架,用户可基于此框架进行二次开发,定制成符合自己的需求的后台管理系统! 详细 运行截图: 项目结构: 详细说明: 环境说明: jdk1.8mavenMySQL5.7 项…...
音乐项目后台管理系统出现的问题
1.当对歌手的歌曲进行编辑时候,会把所有的歌曲信息给修改了。 解决方法:修改controller层的中SongController代码中的这一行代码 boolean flag songService.updateById(song); 2.添加歌曲,在弹出框中输入,没有显示。原因:前端页…...
数据结构——图(图的存储及基本操作)
文章目录 前言一、邻接矩阵法(顺序存储)1.无向图存储邻接矩阵算法2.有向图存储邻接矩阵算法 二、邻接表法(图的链式存储结构)总结 前言 邻接矩阵法(图的顺序存储结构) 1.1 无向图邻接矩阵算法 1.2 有向图邻接矩阵算法邻接表法(图的一种链式存储结构) 一…...
2023年项目管理工具使用趋势分析及预测
随着技术的不断进步以及工作和领导态度的演变,各个行业都在经历着深刻的变革。项目管理领域同样如此,团队项目的技术和人员管理风格及策略正在不断地调整与优化,以适应新冠疫情后所呈现出的新的工作场所格局。在此背景下,以下是我…...
Vue3 实现一个无缝滚动组件(支持鼠标手动滚动)
Vue3 实现一个无缝滚动组件(支持鼠标手动滚动) 前言 在日常开发中,经常遇到需要支持列表循环滚动展示,特别是在数据化大屏开发中,无缝滚动使用频率更为频繁,在jquery时代,我们常用的无缝滚动组…...
【IP数据报】IP地址和MAC地址的区别
1、用IP地址来标识Internet的主机 在每个IP数据报中,都会携带源IP地址和目标IP地址来标识该IP数据报的源和目的主机。IP数据报在传输过程中,每个中间节点(IP 网关)还需要为其选择从源主机到目的主机的合适的转发路径(即路由)。IP协议可以根据路由选择协…...
高并发笔记
如何设计一个高并发系统?:https://mp.weixin.qq.com/s/yFc-70DEhloWn0G3GDa6Yw 分布式 ID 服务实践:https://mp.weixin.qq.com/s/KAts9Zjj8JpEd0Q6pqLlgQ 一文聊透布隆过滤器:https://mp.weixin.qq.com/s/qJ2fDm1Z57bPSzOBrgiqfg …...
eNSP网络学习
一、eNSP 1.什么是eNSP eNSP(Enterprise Network Simulation Platform)是一款由华为提供的免费的、可扩展的、图形化操作的网络仿真工具平台,主要对企业网络路由器、交换机进行软件仿真,完美呈现真实设备实景,支持大型网络模拟,让…...
广州xx策划公司MongoDB恢复-2023.09.09
2023.09.08用户的MongoDB数据库被勒索病毒攻击,数据全部被清空。 提示: mongoDB的默认端口为27017,黑客通常通过全网段扫描27017是否开放判断是否是MongoDB服务器。一旦发现27017开放,黑客就会用空密码、弱密码尝试连接数据库。黑…...
golang --- module-aware 模式下 包引入
一、文件列表如下 其中helloWorld目录是main包(package)所在目录,即该目录下所有的goy源文件(不包含子目录)属于main包,hello.go是mian函数所在文件 二、module-aware 模式启用 开启mod模式 go env -w G…...
从原理到实践 | Pytorch tensor 张量花式操作
文章目录 1.张量形状与维度1.1标量(0维张量):1.2 向量(1维张量):1.3矩阵(2维张量):1.4高维张量: 2. 张量其他创建方式2.1 创建全零或全一张量:2.2…...
无涯教程-JavaScript - TRANSPOSE函数
描述 TRANSPOSE函数将单元格的垂直范围作为水平范围返回,反之亦然。必须将TRANSPOSE函数作为数组公式输入,该范围必须具有与行范围和列范围相同的行和列数。 您可以使用TRANSPOSE在工作表上移动数组或范围的垂直和水平方向。 语法 TRANSPOSE (array)键入函数后,按CTRL SHI…...
Webserver项目解析
一.webserver的组成部分 Buffer类 用于存储需要读写的数据 Channel类 存储文件描述符和相应的事件,当发生事件时,调用对应的回调函数 ChannelMap类 Channel数组,用于保存一系列的Channel Dispatcher 监听器,可以设置为epo…...
Spring Cloud 篇
1、什么是SpringCloud ? Spring Cloud 流应用程序启动器是基于 Spring Boot 的 Spring 集成应用程序,提供与外部系统的集成。Spring cloud Task,一个生命周期短暂的微服务框架,用于快速构建执行有限数据处理的应用程序。 2、什么…...
vim,emacs,verilog-mode这几个到底是啥关系?
vim:不多说了被各类coder誉为地表最强最好用的编辑器;gvim,gui vim的意思; emacs:也是一个编辑器,类似vscode; vim在使用的时候为了增强其功能,有好多好多插件,都是以.…...
中线企业网站建设的问题/哪里的网络推广培训好
http://my.oschina.net/goal/blog/195749?p1 目录[-] 写在前面的话什么是字节序MSB和LSB大端序小端序网络字节序主机字节序总结pack/unpack详解格式字符翻译格式字符详解unpack的用法一些例子PHP作为一门为web而生的服务器端开发语言,被越来越多的公司所采用。其中…...
服务器上如何建设多个网站/网站seo基础优化
网友南京-李先森给了他收集的一些资料,如下: Buckets 对指定列计算 hash,根据 hash 值切分数据,目的是为了并行,每一个 Bucket 对应一个文件。如将 user 列分散至 32 个 bucket,首先对 user 列的值计算 has…...
怎么做网站的快照/澳门seo关键词排名
今天在用SQL Server 2008执行一个SQL脚本文件时,引发类型为“System.OutOfMemoryException”的异常错误,脚本明明是从SQL Server 2008导出的 出现这个错误的主要原因是由于SQL脚本文件太大,估计超过了100M了转载于:https://www.cnblogs.com/x…...
可信赖的深圳网站建设/2021小学生新闻摘抄
/*** 选择排序的思想:* 每次从待排序列中找到最小的元素,* 然后将其放到待排的序列的最左边,直到所有元素有序** 选择排序改进了冒泡排序,将交换次数从O(N^2)减少到O(N)* 不过比较次数还是O(N)*/package al;public class SelectSo…...
wordpress 4.4.2 中文/网站关键词排名外包
视频简介:该视频介绍emWin底层驱动接口。 源视频包下载地址:链接:http://pan.baidu.com/s/1nvPpC2d 密码:cbb7银杏科技优酷视频发布区:http://i.youku.com/gingko8...
wordpress 获取置顶文章/什么网站可以发布广告
WebStorm 之 Cordova 环境搭建 原文:WebStorm 之 Cordova 环境搭建一、环境搭建 Cordova 环境配置之前,应先下载安装 Node.js ,中文官网:http://nodejs.cn/。 以管理员身份运行 cmd 命令行工具: 1、查看 Node.js 是否已安装成功&a…...